comparison Squawk.lua @ 18:a3328fffef5c

Split of main file into components
author wobin
date Thu, 07 May 2009 02:52:23 +1000
parents 2a73deb7bc54
children 431f2fce08f2
comparison
equal deleted inserted replaced
17:2a73deb7bc54 18:a3328fffef5c
23 Pending = {}, 23 Pending = {},
24 Requested = {}, 24 Requested = {},
25 Blocked = {}, 25 Blocked = {},
26 } 26 }
27 } 27 }
28 local SquawkViewMeta = { __tostring = function() return "SquawkView" end }
28 29
29 function Squawk:OnInitialize() 30 function Squawk:OnInitialize()
30 Model.db = LibStub("AceDB-3.0"):New("SquawkDB", defaults) 31 Model.db = LibStub("AceDB-3.0"):New("SquawkDB", defaults)
31 Model.Squawks = Model.db.profile.Squawks 32 Model.Squawks.Main = Model.db.profile.Squawks
32 Settings.Follower = Model.db.profile.Follower 33 Settings.Follower = Model.db.profile.Follower
33 Settings.Following = Model.db.profile.Following 34 Settings.Following = Model.db.profile.Following
34 Settings.Pending = Model.db.profile.Pending 35 Settings.Pending = Model.db.profile.Pending
35 Settings.Requested = Model.db.profile.Requested 36 Settings.Requested = Model.db.profile.Requested
36 Settings.Blocked = Model.db.profile.Blocked 37 Settings.Blocked = Model.db.profile.Blocked
38 39
39 LibStub("AceComm-3.0"):Embed(Controller) 40 LibStub("AceComm-3.0"):Embed(Controller)
40 LibStub("AceTimer-3.0"):Embed(Controller) 41 LibStub("AceTimer-3.0"):Embed(Controller)
41 Controller:RegisterComm("Squawk", Controller.ReceiveMessage) 42 Controller:RegisterComm("Squawk", Controller.ReceiveMessage)
42 LibStub("AceConsole-3.0"):Embed(View) 43 LibStub("AceConsole-3.0"):Embed(View)
44 setmetatable(View, SquawkViewMeta)
43 45
46 Model.Squawks.Reload(Model.Squawks) -- Retrain the table lookups
44 end 47 end
45
46 -- Model --
47 --[[
48 --Each Squawk will have the following information:
49 -- * Owner (Name)
50 -- * Time (Epoch)
51 -- * Message (140 characters)
52 -- * ReplyTo (Name)
53 -- * Related (Names)
54 --
55 -- Each User will have the following lists:
56 -- * Follower
57 -- * Following
58 -- * Blocked
59 -- * Pending (Requests to follow that you haven't acted on)
60 -- * Requested (Requests to follow that you have made)
61 -- * Privacy State
62 --
63 -- A user can only request to follow an online person. Requests can be approved
64 -- on or offline, but the initial request must be made online.
65 --
66 -- When a user makes a request to follow a private user, the subsequent paths occur:
67 -- - Followee is added to Settings.Requested
68 -- - Followee receives 'follow request' -> (their) Settings.Pending
69 -- - Followee acts on request -> (their) Settings.Pending cleared
70 -- 1) Follwer is online
71 -- - Follower receives 'request accepted' -> Added to Settings.Following and
72 -- cleared from Settings.Requested
73 -- 2) Follower is offline
74 -- - The next time Follower is online and recieves a Squawk we check if there
75 -- is a Settings.Requested for that name, and if so assume they have approved
76 -- and clear/add records appropriately.
77 --
78 -- For updating, there can be a few methods.
79 --
80 -- Guild open: you're not private, you broadcast to the guild your last X
81 -- squawks on login
82 --
83 -- followers:
84 --]]
85 Model.Squawks = {}
86 local Squawks = Model.Squawks
87 Squawks.Main = {}
88 Squawks.Owners = {}
89
90 local function wrap(str, limit)
91 limit = limit or 72
92 local here = 1
93 return str:gsub("(%s+)()(%S+)()",
94 function(sp, st, word, fi)
95 if fi-here > limit then
96 here = st
97 return "\n"..word
98 end
99 end)
100 end
101
102 function Squawks:new(Message, Owner)
103 local o = {}
104 o.Owner = Owner or UnitName("player")
105 o.Message = wrap(Message)
106 o.Time = time()
107 local reply, to = strsplit("@", ((strsplit(" ", Message))))
108 if reply == "" then
109 o.ReplyTo = to
110 end
111
112 o.Related = {}
113
114 for word in string.gmatch(Message, "@(%a+)") do
115 if word ~= o.ReplyTo or "" then
116 table.insert(o.Related, word)
117 end
118 end
119
120 table.insert(self.Main, o)
121
122 if not self.Owners[Owner] then
123 self.Owners[Owner] = {}
124 end
125 table.insert(self.Owners[Owner], o)
126
127 return o
128 end
129
130 function Squawks:Sort(Squawks)
131 table.sort(Squawks or self.Main, function(a,b) return a.Time > b.Time end)
132 return Squawks or self.Main
133 end
134
135 function Squawks:GetOwn(Squawks)
136 local mine = {}
137 for _, squawk in ipairs(Squawks or self.Main) do
138 if squawk.Owner == UnitName("player") then
139 table.insert(mine, squawk)
140 end
141 end
142 return self:Sort(mine)
143 end
144
145 function Squawks:GetLast10(Squawks)
146 local mine = {}
147 Squawks = Squawks or self.Main
148 local limit = #Squawks < 10 and #Squawks or 10
149
150 Squawks = Squawk:Sort(Squawks)
151
152 for i=1,limit do
153 table.insert(mine, Squawks[i])
154 end
155 return mine
156 end
157
158 -- initially called with no arguments to get the latest timestamp of
159 -- my squawks, or with a name to find the latest timestamp of all
160 -- squawks from that user
161 function Squawks:GetLatestTimestamp(Name, Squawks)
162 if Name then
163 if self.Owners[Name] then
164 return self:GetLatestTimestamp(nil, self.Owners[Name])
165 else
166 return -1 -- No squawks exist for that name in our records
167 end
168 end
169
170 Squawks = Squawks or self.Main or {}
171 local latest = self:Sort(Squawks)
172 return latest and #latest > 0 and latest[1].Time or -1
173 end
174
175 function Squawks:GetLatestSquawks(Timestamp)
176 local latest = {}
177 for i, squawk in ipairs(self:Sort()) do
178 if squawk.Time > Timestamp and i < 10 then
179 table.insert(latest, squawk)
180 else
181 return latest
182 end
183 end
184 end
185
186 function Settings:IsPrivate()
187 return Settings.Private
188 end
189
190 function Settings:TogglePrivate()
191 Settings.Private = not Settings.Private
192 end
193
194 function Settings:AddFollower(Name)
195 Settings.Follower[Name] = 1
196 self:RemovePending(Name)
197 end
198
199 function Settings:AddFollowing(Name)
200 Settings.Following[Name] = 1
201 self:RemoveRequested(Name)
202 end
203
204 function Settings:AddBlock(Name)
205 Settings.Blocked[Name] = 1
206 self:RemoveFollower(Name)
207 self:RemoveFollowing(Name)
208 end
209
210 function Settings:AddPending(Name)
211 Settings.Pending[Name] = 1
212 end
213
214 function Settings:AddRequested(Name)
215 Settings.Requested[Name] = 1
216 end
217
218 function Settings:RemoveFollower(Name)
219 if Settings.Follower[Name] then
220 Settings.Follower[Name] = nil
221 end
222 end
223
224 function Settings:RemoveFollowing(Name)
225 if Settings.Following[Name] then
226 Settings.Following[Name] = nil
227 end
228 end
229
230 function Settings:RemoveBlock(Name)
231 if Settings.Blocked[Name] then
232 Settings.Blocked[Name] = nil
233 end
234 end
235
236 function Settings:RemovePending(Name)
237 if Settings.Pending[Name] then
238 Settings.Pending[Name] = nil
239 end
240 end
241
242 function Settings:RemoveRequested(Name)
243 if Settings.Requested[Name] then
244 Settings.Requested[Name] = nil
245 end
246 end
247
248 --Controller--
249
250 function Controller:TheyWantToFollowMe(Name)
251 if Settings:IsPrivate() then
252 Settings:AddPending(Name)
253 self:PutForwardFollowRequest(Name)
254 self:SendMessageToTarget(Name, "#Pending|"..UnitName("player"))
255 else
256 Settings:AddFollower(Name)
257 View:NotifyOfNewFollower(Name)
258 self:SendMessageToTarget(Name, "#Follow|"..UnitName("player"))
259 end
260 end
261
262 function Controller:TheyWantToUnfollowMe(Name)
263 Settings:RemoveFollower(Name)
264 end
265
266 function Controller:IWantToFollowThem(Name)
267 self:SendMessageToTarget(Name, "#Request|"..UnitName("player"))
268 Settings:AddRequested(Name)
269 end
270
271 function Controller:IWantToUnfollowThem(Name)
272 Settings:RemoveFollowing(Name)
273 self:SendMessageToTarget(Name, "#Unfollow|"..UnitName("player"))
274 View:NotifyOfUnfollowing(Name)
275 end
276
277 function Controller:IAmNowFollowingThem(Name)
278 Settings:AddFollowing(Name)
279 View:NotifyOfNewFollowing(Name)
280 end
281
282 function Controller:AddANewSquawk(Name, Message, Source)
283 if not Settings.Blocked[Name] then
284
285 if Source == "WHISPER" then
286 if Settings.Requested[Name] then -- We've been approved offline!
287 Settings:AddFollowing(Name)
288 end
289
290 if not Settings.Following[Name] then -- If we're no longer following this person
291 self:SendMessageToTarget(Name, "#Unfollow|"..UnitName("player"))
292 return
293 end
294 end
295
296 if Source == "GUILD" and Name == UnitName("player") then
297 return
298 end
299
300 table.insert(Model.Squawks, Squawk:new(Message, Name))
301 View:UpdateSquawkList()
302 end
303 end
304
305 local trigger
306 local function RepressFailure(frame, event, ...)
307 if arg1:match(string.gsub(ERR_CHAT_PLAYER_NOT_FOUND_S, "%%s", "(.*)")) then
308 if trigger then Controller:CancelTimer(trigger, true) end
309 trigger = Controller:ScheduleTimer(
310 function()
311 ChatFrame_RemoveMessageEventFilter("CHAT_MSG_SYSTEM", RepressFailure)
312 end, 3) -- Give it three seconds and then remove the filter.
313 return true
314 else
315 return false, unpack(...)
316 end
317 end
318
319 function Controller:SendNewSquawk(Message)
320 if not Settings:IsPrivate() then
321 self:SendMessageToGuild("#Squawk|"..UnitName("player").."|"..Message)
322 end
323
324 self:AddANewSquawk(UnitName("player"), Message)
325 for name, _ in pairs(Settings.Following) do
326 self:SendMessageToTarget(name, "#Squawk|"..UnitName("player").."|"..Message)
327 end
328 end
329
330 function Controller:ImPending(Name)
331 View:NotifyOfPending(Name)
332 end
333
334 function Controller:PutForwardFollowRequest(Name)
335 View:NotifyOfPendingRequest(Name)
336 end
337
338 function Controller:ApprovePendingRequest(Name)
339 Settings:AddFollower(Name)
340 View:NotifyOfNewFollower(Name)
341 self:SendMessageToTarget(Name, "#Follow|"..UnitName("player"))
342 end
343
344
345
346 function Controller:SendMessageToTarget(Name, Message)
347 ChatFrame_AddMessageEventFilter("CHAT_MSG_SYSTEM", RepressFailure)
348 self:SendCommMessage("Squawk", Message, "WHISPER", Name)
349 end
350
351 function Controller:SendMessageToGuild(Message)
352 self:SendCommMessage("Squawk", Message, "GUILD")
353 end
354
355 local Parse = {
356 ["#Pending"] = Controller.ImPending,
357 ["#Follow"] = Controller.IAmNowFollowingThem,
358 ["#Unfollow"] = Controller.TheyWantToUnfollowMe,
359 ["#Squawk"] = Controller.AddANewSquawk,
360 ["#Request"] = Controller.TheyWantToFollowMe,
361 }
362
363 function Controller:ReceiveMessage(Message, Distribution, Sender)
364 local command, name, info = strsplit("|",Message)
365 View:Print(Distribution..":"..Message)
366 Parse[command](Controller, name, info, Distribution)
367 end
368
369 -- View --
370
371 function View:UpdateSquawkList()
372 self:Print("Updated Squawk List")
373 self:ShowMeMySquawks()
374 end
375
376 function View:NotifyOfPending(Name)
377 self:Print(Name.." will have to approve your request")
378 end
379
380 function View:NotifyOfPendingRequest(Name)
381 self:Print(Name.." wants to follow you.")
382 end
383
384 function View:NotifyOfNewFollowing(Name)
385 self:Print("You are now following "..Name)
386 end
387
388 function View:NotifyOfUnfollowing(Name)
389 self:Print("You are no longer following "..Name)
390 end
391
392 function View:NotifyOfNewFollower(Name)
393 self:Print(Name.." is now following you")
394 end
395
396 function View:ShowMeMySquawks()
397 for _,squawk in ipairs(Model.Squawks.Main) do
398 self:Print(squawk.Message)
399 end
400 end
401
402 function View:ShowMeMyFollowers()
403 self:Print("My followers are:")
404 for name,_ in pairs(Settings.Follower) do
405 self:Print(name)
406 end
407 end
408
409 function View:ShowMeWhoImFollowing()
410 self:Print("I am following:")
411 for name,_ in pairs(Settings.Following) do
412 self:Print(name)
413 end
414 end
415
416 function View:ShowMeWhoIveBlocked()
417 self:Print("I've blocked:")
418 for name,_ in pairs(Settings.Blocked) do
419 self:Print(name)
420 end
421 end
422
423 local TimeSpan = { [1] = {"second", 60, 1},
424 [2] = {"minute", 3600, 60},
425 [3] = {"hour", 86400, 3600} }
426
427 function View:GetTime(stime)
428 local lapsed = difftime(time(), stime)
429 if lapsed < 86400 then -- if we're still in the same day...
430 for _,span in ipairs(TimeSpan) do
431 if lapsed < span[2] then
432 local timespan = math.floor(lapsed/span[3])
433 if timespan == 1 then
434 timespan = timespan .." ".. span[1]
435 else
436 timespan = timespan .. " ".. span[1].."s"
437 end
438 return timespan.. " ago"
439 end
440 end
441 end
442 return date("%I:%M %p %b %d", stime)
443 end
444
445 local LDBFeed = LibStub("LibDataBroker-1.1"):NewDataObject("Squawk", {type = "data source", text = "Awk!"})
446 local QTip = LibStub("LibQTip-1.0")
447 local QTipClick = LibStub("LibQTipClick-1.0")
448 local tooltip = {}
449
450 local function HideTooltip()
451 if MouseIsOver(tooltip) then return end
452 tooltip:SetScript("OnLeave", nil)
453 tooltip:Hide()
454 QTip:Release(tooltip)
455 tooltip = nil
456 end
457
458 local function ReplyToMe(cell, Owner, event)
459 View:Print("Replying to @"..Owner)
460 end
461
462 local function AddLine(tooltip, Line, Number, Owner, TimeStamp)
463 local x,y
464 if #Line < 79 then
465 y,x = tooltip:AddNormalLine(Number, Owner, Line, TimeStamp)
466 else
467 y,x = tooltip:AddNormalLine(Number, Owner, Line:sub(1, 80).."-", TimeStamp)
468 AddLine(tooltip, Line:sub(81))
469 end
470 if not TimeStamp then return end
471
472 -- Now add the reply clickback
473 tooltip:SetCell(y, 5, " ", Owner)
474 tooltip.lines[y].cells[5]:SetBackdrop({bgFile= "Interface\\Addons\\Squawk\\reply"})
475 if not tooltip.lines[y].cells[5]:GetScript("OnHide") then
476 tooltip.lines[y].cells[5]:SetScript("OnHide", function(self) self:SetBackdrop(nil) self:SetScript("OnHide", nil) end)
477 end
478 -- Reply clickback finished
479 end
480
481 function LDBFeed:OnEnter()
482 tooltip = QTipClick:Acquire("Squawk",5, "LEFT", "CENTER", "LEFT", "RIGHT", "RIGHT")
483 tooltip:Clear()
484 tooltip:SetCallback("OnMouseDown", ReplyToMe)
485 self.tooltip = tooltip
486 for i,squawk in ipairs(Squawk:GetLast10(Model.Squawks)) do
487 local head = true
488 local message = {strsplit("\n",squawk.Message)}
489 for _,line in ipairs(message) do
490 if head then
491 AddLine(tooltip, line, i..".", squawk.Owner, View:GetTime(squawk.Time))
492 head = false
493 else
494 AddLine(tooltip, line)
495 end
496 end
497 end
498 tooltip:SmartAnchorTo(self)
499 tooltip:SetScript("OnLeave", HideTooltip)
500 tooltip:Show()
501 end
502
503 function LDBFeed:OnLeave()
504 HideTooltip()
505 end
506 --[[
507
508 function LDBFeed:OnClick(button)
509 editbox:ClearAllPoints()
510 editbox:SetPoint(GetTipAnchor(self))
511 editbox:Show()
512 end
513
514 local function GetTipAnchor(frame)
515 if not x or not y then return "TOPLEFT", frame, "BOTTOMLEFT" end
516 local hhalf = (x > UIParent:GetWidth()*2/3) and "RIGHT" or (x < UIParent:GetWidth()/3) and "LEFT" or ""
517 local vhalf = (y > UIParent:GetHeight()/2) and "TOP" or "BOTTOM"
518 return vhalf..hhalf, frame, (vhalf == "TOP" and "BOTTOM" or "TOP")..hhalf
519 end
520
521 local editbox = CreateFrame('EditBox', nil, UIParent)
522 editbox:Hide()
523 editbox:SetAutoFocus(true)
524 editbox:SetHeight(32)
525 editbox:SetWidth(350)
526 editbox:SetFrameStrata("HIGH")
527 editbox:SetFontObject('GameFontHighlightSmall')
528 lib.editbox = editbox
529
530 editbox:SetScript("OnEscapePressed", editbox.ClearFocus)
531 editbox:SetScript("OnEnterPressed", editbox.ClearFocus)
532 editbox:SetScript("OnEditFocusLost", editbox.Hide)
533 editbox:SetScript("OnEditFocusGained", editbox.HighlightText)
534 editbox:SetScript("OnTextChanged", function(self)
535 self:SetText(self:GetParent().val)
536 self:HighlightText()
537 end)
538
539 local left = editbox:CreateTexture(nil, "BACKGROUND")
540 left:SetWidth(8) left:SetHeight(20)
541 left:SetPoint("LEFT", -5, 0)
542 left:SetTexture("Interface\\Common\\Common-Input-Border")
543 left:SetTexCoord(0, 0.0625, 0, 0.625)
544
545 local right = editbox:CreateTexture(nil, "BACKGROUND")
546 right:SetWidth(8) right:SetHeight(20)
547 right:SetPoint("RIGHT", 0, 0)
548 right:SetTexture("Interface\\Common\\Common-Input-Border")
549 right:SetTexCoord(0.9375, 1, 0, 0.625)
550
551 local center = editbox:CreateTexture(nil, "BACKGROUND")
552 center:SetHeight(20)
553 center:SetPoint("RIGHT", right, "LEFT", 0, 0)
554 center:SetPoint("LEFT", left, "RIGHT", 0, 0)
555 center:SetTexture("Interface\\Common\\Common-Input-Border")
556 center:SetTexCoord(0.0625, 0.9375, 0, 0.625)
557
558 function lib.OpenEditbox(self)
559 editbox:SetText(self.val)
560 editbox:SetParent(self)
561 editbox:SetPoint("LEFT", self)
562 editbox:SetPoint("RIGHT", self)
563 editbox:Show()
564 end
565 --]]
566