1 module tweet_table; 2 3 import std.uri : encodeComponent; 4 import std.algorithm : map; 5 import std.utf : UTFException; 6 7 import carbon.utils : toLiteral; 8 import carbon.templates; 9 10 import awebview.gui.html, 11 awebview.gui.activity; 12 13 import awebview.wrapper; 14 15 import msgpack; 16 17 18 immutable HTMLTableFormat = q{ 19 <div class="col-md-6"> 20 <table class="table table-striped" id="%[id%]"> 21 <thead><tr></tr></thead> 22 <tbody></tbody> 23 </table> 24 </div> 25 }; 26 27 28 /** 29 need jQuery 30 */ 31 class TweetTable : TemplateHTMLElement!(HTMLElement, HTMLTableFormat) 32 { 33 this(string id) 34 { 35 super(id, true); 36 theaders = ["image", "username", "tweet"]; 37 } 38 39 40 @property 41 auto theaders() 42 { 43 static struct THeaders 44 { 45 @property const(string[]) array() const { return _parent._ths; } 46 string opIndex(size_t idx) const { return _parent._ths[idx]; } 47 48 49 void opIndexAssign(string th, size_t idx) 50 { 51 _parent._ths[idx] = th; 52 _parent.theaders = _parent._ths; 53 } 54 55 56 void opOpAssign(string op : "~")(string th) 57 { 58 this ~= [th]; 59 } 60 61 62 void opOpAssign(string op : "~")(string[] ths) 63 { 64 _parent._ths ~= ths; 65 _parent.theaders = _parent._ths; 66 } 67 68 69 private: 70 TweetTable _parent; 71 } 72 73 return THeaders(this); 74 } 75 76 77 @property 78 void theaders(string[] ths) 79 { 80 _ths = ths; 81 if(this.activity !is null) 82 rerenderingHeader(); 83 } 84 85 86 override 87 void onStart(HTMLPage page) 88 { 89 if(auto p = this.id in page.activity.application.savedData){ 90 ubyte[] myData = *p; 91 92 auto pf = unpack!PackedField(*p); 93 this._ths = pf.ths; 94 this._tds = pf.tds; 95 96 if(this._tds.length > 100) 97 this._tds = this._tds[$-100 .. $]; 98 99 *p = pf.parentData; 100 super.onStart(page); 101 *p = myData; 102 } 103 else 104 { 105 super.onStart(page); 106 } 107 } 108 109 110 override 111 void onDestroy() 112 { 113 auto app = this.activity.application; 114 115 super.onDestroy(); 116 117 PackedField pf; 118 pf.ths = _ths; 119 pf.tds = _tds; 120 121 if(pf.tds.length > 100) 122 pf.tds = pf.tds[$-100 .. $]; 123 124 if(auto p = this.id in app.savedData) 125 pf.parentData = *p; 126 127 app.savedData[this.id] = pack(pf); 128 } 129 130 131 override 132 void onLoad(bool isInit) 133 { 134 super.onLoad(isInit); 135 136 rerenderingHeader(); 137 rerenderingBody(); 138 } 139 140 141 void rerenderingHeader() 142 { 143 auto app = appender!string(); 144 app.formattedWrite("%-(<th>%s</th>%|%)", _ths.map!encodeComponent()); 145 146 activity[$(mixin(Lstr!"#%[id%] > thead > tr"))].innerHTML = app.data; 147 } 148 149 150 void rerenderingBody() 151 { 152 auto app = appender!string(); 153 foreach_reverse(e; _tds){ 154 try 155 appendTR(app, e[0], e[1], e[2]); 156 catch(UTFException ex) 157 appendTR(app, e[0], e[1], ex.to!string); 158 } 159 160 activity[$(mixin(Lstr!"#%[id%] > tbody"))].innerHTML = app.data; 161 } 162 163 164 void addTweet(string imageURL, string username, string tweet) 165 { 166 _tds ~= [imageURL, username, tweet]; 167 if(this.activity !is null){ 168 auto app = appender!string(); 169 appendTR(app, imageURL, username, tweet); 170 171 activity[$(mixin(Lstr!"#%[id%] > tbody"))].prepend(app.data); 172 } 173 } 174 175 176 private 177 void appendTR(Writer)(ref Writer w, string imageURL, string username, string tweet) 178 { 179 w.put(mixin(Lstr!`<tr><td><img src=%[toLiteral(imageURL)%]></td><td>%[username%]</td><td>%[tweet%]</td></tr>`)); 180 } 181 182 183 private: 184 string[] _ths; 185 string[3][] _tds; 186 187 struct PackedField { string[] ths; string[3][] tds; ubyte[] parentData; } 188 }