Mercurial > wow > squawk
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 |