EditorPersonal.swift 476 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763
  1. //
  2. // EditorPersonal.swift
  3. // Qmera
  4. //
  5. // Created by Akhmad Al Qindi Irsyam on 31/08/21.
  6. //
  7. import UIKit
  8. import AVKit
  9. import AVFoundation
  10. import QuickLook
  11. import NotificationBannerSwift
  12. import Photos
  13. import nuSDKService
  14. import SwiftLinkPreview
  15. import SDWebImage
  16. import PhotosUI
  17. public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestureRecognizerDelegate, CLLocationManagerDelegate {
  18. @IBOutlet var viewButton: UIView!
  19. @IBOutlet var constraintViewTextField: NSLayoutConstraint!
  20. @IBOutlet var buttonVoice: UIButton!
  21. @IBOutlet var buttonSendImage: UIButton!
  22. @IBOutlet var buttonSendPhoto: UIButton!
  23. @IBOutlet var buttonSendSticker: UIButton!
  24. @IBOutlet var buttonSendFile: UIButton!
  25. @IBOutlet var textFieldSend: UITextView!
  26. @IBOutlet var heightTextFieldSend: NSLayoutConstraint!
  27. @IBOutlet var buttonSendChat: UIButton!
  28. @IBOutlet var tableChatView: UITableView!
  29. @IBOutlet var constraintTopTextField: NSLayoutConstraint!
  30. @IBOutlet var constraintBottomAttachment: NSLayoutConstraint!
  31. @IBOutlet var viewTextfield: UIView!
  32. @IBOutlet weak var buttonAckConfidential: UIButton!
  33. @IBOutlet weak var constraintLeftTextField: NSLayoutConstraint!
  34. @IBOutlet weak var constraintBottomTableViewWithTextfield: NSLayoutConstraint!
  35. @IBOutlet weak var viewAttachment: UIStackView!
  36. public var dataPerson: [String: String?] = [:]
  37. var dataMessages: [[String: Any?]] = []
  38. var dataDates: [String] = []
  39. var users: [User] = []
  40. public var dataMessageForward: [[String: Any?]]?
  41. var imageVideoPicker: ImageVideoPicker!
  42. var documentPicker: DocumentPicker!
  43. var currentIndexpath: IndexPath?
  44. var previewItem: NSURL?
  45. var reffId: String?
  46. var stickers = [String]()
  47. public var unique_l_pin = ""
  48. public var isContactCenter = false
  49. public var isRequestContactCenter = true
  50. public var fromNotification = false
  51. public var onGoingCC = false
  52. public var fPinContacCenter = ""
  53. public var complaintId = ""
  54. public var referenceMessageId = ""
  55. public var referenceChatDate = ""
  56. var channelContactCenter = ""
  57. var counter = 0
  58. var dateStartCC = ""
  59. var markerCounter: String?
  60. var buttonScrollToBottom = UIButton()
  61. let indicatorCounterBSTB = UIView()
  62. let labelCounter = UILabel()
  63. var copySession = false
  64. var forwardSession = false
  65. var deleteSession = false
  66. var isSearching = false
  67. let containerMultpileSelectSession = UIView()
  68. let containerAction = UIView()
  69. var removed = false
  70. var isConfidential = false
  71. var isAck = false
  72. var isSecret = false
  73. let viewSticker = UIView()
  74. let containerLink = UIView()
  75. let containerPreviewReply = UIView()
  76. var bottomAnchorPreviewReply = NSLayoutConstraint()
  77. var blocking = ""
  78. var timeoutCC = Timer()
  79. var nowSelectedCategoryCC = ""
  80. var showToastTwiceClick = false
  81. var showToast30s = false
  82. var allowTyping = true
  83. var hapticSwipeLeft = false
  84. var listTimerCredential: [String: Int] = [:]
  85. var timerCredential: [String: Timer] = [:]
  86. let contactChatNav = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "contactChatNav") as! UINavigationController
  87. var searchBar: UISearchBar!
  88. var constraintBottomContainerMultpileSelectSession = NSLayoutConstraint()
  89. var titleSearchMatches: UILabel!
  90. var textSearch = ""
  91. var countMatchesSearch = 0
  92. var lastScrollIdxSearch = 0
  93. var buttonUp: UIButton!
  94. var buttonDown: UIButton!
  95. var multipleOffsetUp = 1
  96. var lastOffsetDown = 1
  97. var gettingDataMessage = true
  98. var showingLink = ""
  99. var isAlwaysHideLinkPreview = false
  100. var timerCheckLink: Timer?
  101. var timerFakeProgress: Timer?
  102. var showMenuContext = false
  103. var touchedSubview = UIView()
  104. var listViewOnSection: [UIView] = []
  105. var fromVCAC = false
  106. var serviceIdCC = ""
  107. var isDirectCC = false
  108. var fakeProgMultip = 0
  109. let maxFakeProgMultip = 2
  110. var groupImages: [String:[ImageGrouping]] = [:]
  111. var titleText: String!
  112. var lastY: CGFloat = 0
  113. var audioPlayer: AVAudioPlayer?
  114. var editVC = UIViewController()
  115. var editTextView = UITextView()
  116. var isEditingMessage = false
  117. var constraintBottomeditTextView: NSLayoutConstraint!
  118. var constraintHeighteditTextView: NSLayoutConstraint!
  119. var constraintBottomSendEditTV: NSLayoutConstraint!
  120. let locationManager = CLLocationManager()
  121. var longitude = ""
  122. var latitude = ""
  123. var isBlackCancelButton = false
  124. public override func viewDidDisappear(_ animated: Bool) {
  125. if self.isMovingFromParent {
  126. removeAllObjectBeforeDismissVC()
  127. }
  128. }
  129. public override func viewDidAppear(_ animated: Bool) {
  130. let navBarAppearance = UINavigationBarAppearance()
  131. navBarAppearance.configureWithOpaqueBackground()
  132. navBarAppearance.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : .mainColor
  133. navigationController?.navigationBar.standardAppearance = navBarAppearance
  134. navigationController?.navigationBar.scrollEdgeAppearance = navBarAppearance
  135. navigationController?.navigationBar.isTranslucent = false
  136. navigationController?.navigationBar.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : .mainColor
  137. navigationController?.navigationBar.tintColor = .white
  138. navigationController?.navigationBar.overrideUserInterfaceStyle = .dark
  139. navigationController?.navigationBar.barStyle = .black
  140. if self.navigationController?.isNavigationBarHidden ?? false {
  141. self.navigationController?.setNavigationBarHidden(false, animated: false)
  142. }
  143. updateProfile()
  144. gettingDataMessage = false
  145. let indexPath = tableChatView.indexPathsForVisibleRows?.first
  146. if indexPath != nil {
  147. let headerRect = tableChatView.rectForHeader(inSection: indexPath!.section)
  148. let isPinned = headerRect.origin.y <= tableChatView.contentOffset.y
  149. if listViewOnSection.count != 0 && listViewOnSection.count - 1 == indexPath!.section && isPinned {
  150. let sect = listViewOnSection.count - 1 < currentIndexpath!.section ? listViewOnSection.count - 1 : currentIndexpath!.section
  151. let headerView = listViewOnSection[sect]
  152. headerView.isHidden = true
  153. }
  154. }
  155. }
  156. public override func viewDidLoad() {
  157. super.viewDidLoad()
  158. // navigationController?.navigationBar.topItem?.title = ""
  159. Utils.addBackground(view: contactChatNav.view)
  160. if Nexilis.fromMAB {
  161. Nexilis.floatingButton.isHidden = true
  162. }
  163. viewButton.layer.shadowColor = self.traitCollection.userInterfaceStyle == .dark ? UIColor.white.cgColor : UIColor.gray.cgColor
  164. viewButton.layer.shadowOpacity = 1
  165. viewButton.layer.shadowOffset = .zero
  166. viewButton.layer.shadowRadius = 3
  167. viewButton.addTopBorder(with: UIColor.lightGray, andWidth: 1.0)
  168. // buttonVoice.setImage(resizeImage(image: UIImage(named: "Voice-Record", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, targetSize: CGSize(width: 30, height: 30)), for: .normal)
  169. buttonSendImage.setImage(resizeImage(image: UIImage(named: "Send-Image", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, targetSize: CGSize(width: 30, height: 30)).withTintColor(self.traitCollection.userInterfaceStyle == .dark ? .white : .mainColor), for: .normal)
  170. buttonSendPhoto.setImage(resizeImage(image: UIImage(named: "Camera", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, targetSize: CGSize(width: 30, height: 30)).withTintColor(self.traitCollection.userInterfaceStyle == .dark ? .white : .mainColor), for: .normal)
  171. buttonSendSticker.setImage(resizeImage(image: UIImage(named: "Sticker---Emoji", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, targetSize: CGSize(width: 30, height: 30)).withTintColor(self.traitCollection.userInterfaceStyle == .dark ? .white : .mainColor), for: .normal)
  172. buttonSendFile.setImage(resizeImage(image: UIImage(named: "File---Documents", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, targetSize: CGSize(width: 30, height: 30)).withTintColor(self.traitCollection.userInterfaceStyle == .dark ? .white : .mainColor), for: .normal)
  173. buttonSendChat.setImage(resizeImage(image: self.traitCollection.userInterfaceStyle == .dark ? UIImage(named: "Send-(White)", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(.blackDarkMode) : UIImage(named: "Send-(White)", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, targetSize: CGSize(width: 30, height: 30)).withRenderingMode(.alwaysOriginal), for: .normal)
  174. buttonSendChat.circle()
  175. buttonSendChat.addTarget(self, action: #selector(sendTapped), for: .touchUpInside)
  176. buttonSendChat.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .mainColor
  177. if isContactCenter {
  178. buttonAckConfidential.isHidden = true
  179. constraintLeftTextField.constant = 20
  180. } else {
  181. buttonAckConfidential.circle()
  182. buttonAckConfidential.addTarget(self, action: #selector(showChooserACKConfidential), for: .touchUpInside)
  183. buttonAckConfidential.tintColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : .white
  184. buttonAckConfidential.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .mainColor
  185. }
  186. textFieldSend.layer.cornerRadius = textFieldSend.maxCornerRadius()
  187. textFieldSend.layer.borderWidth = 1.0
  188. textFieldSend.text = "Send message".localized()
  189. textFieldSend.textColor = UIColor.lightGray
  190. textFieldSend.tintColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
  191. textFieldSend.textContainerInset = UIEdgeInsets(top: 12, left: 20, bottom: 11, right: 40)
  192. textFieldSend.layer.borderColor = UIColor.lightGray.withAlphaComponent(0.5).cgColor
  193. textFieldSend.font = UIFont.systemFont(ofSize: 12)
  194. textFieldSend.delegate = self
  195. textFieldSend.allowsEditingTextAttributes = true
  196. navigationItem.rightBarButtonItem?.tintColor = UIColor.secondaryColor
  197. imageVideoPicker = ImageVideoPicker(presentationController: self, delegate: self)
  198. documentPicker = DocumentPicker(presentationController: self, delegate: self)
  199. let fm = FileManager.default
  200. if Bundle.resourceBundle(for: Nexilis.self).url(forResource: "pb_gpt_bot", withExtension: "gif") != nil {
  201. let path = Bundle.resourceBundle(for: Nexilis.self).resourcePath! //resourcesMediaBundle
  202. let items = try! fm.contentsOfDirectory(atPath: path)
  203. for item in items {
  204. if item.hasPrefix("sticker") {
  205. stickers.append(item)
  206. }
  207. }
  208. } else {
  209. let path = Bundle.resourcesMediaBundle(for: Nexilis.self).resourcePath! //resourcesMediaBundle
  210. let items = try! fm.contentsOfDirectory(atPath: path)
  211. for item in items {
  212. if item.hasPrefix("sticker") {
  213. stickers.append(item)
  214. }
  215. }
  216. }
  217. tableChatView.register(UITableViewCell.self, forCellReuseIdentifier: "cellEditorPersonal")
  218. loadData()
  219. setRightButtonItem()
  220. let center: NotificationCenter = NotificationCenter.default
  221. center.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
  222. center.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
  223. center.addObserver(self, selector: #selector(onReceiveMessage(notification:)), name: NSNotification.Name(rawValue: Nexilis.listenerReceiveChat), object: nil)
  224. center.addObserver(self, selector: #selector(onStatusChat(notification:)), name: NSNotification.Name(rawValue: Nexilis.listenerStatusChat), object: nil)
  225. center.addObserver(self, selector: #selector(onUploadChat(notification:)), name: NSNotification.Name(rawValue: "onUploadChat"), object: nil)
  226. center.addObserver(self, selector: #selector(onUnfriend(notification:)), name: NSNotification.Name(rawValue: "onUpdatePersonInfo"), object: nil)
  227. center.addObserver(self, selector: #selector(onTyping(notification:)), name: NSNotification.Name(rawValue: Nexilis.listenerTypingChat), object: nil)
  228. center.addObserver(self, selector: #selector(onFailedSendMessage(notification:)), name: NSNotification.Name(rawValue: Nexilis.failedSendMessage), object: nil)
  229. locationManager.delegate = self
  230. locationManager.requestWhenInUseAuthorization()
  231. DispatchQueue.global().async { [self] in
  232. // Check if location services are enabled
  233. if CLLocationManager.locationServicesEnabled() {
  234. locationManager.desiredAccuracy = kCLLocationAccuracyBest
  235. locationManager.startUpdatingLocation()
  236. } else {
  237. print("Location services are not enabled.")
  238. }
  239. }
  240. if dataMessageForward != nil {
  241. for i in 0..<dataMessageForward!.count {
  242. let isForwarded = (dataMessageForward![i][TypeDataMessage.is_forwarded] as? Int) ?? 0
  243. sendChat(message_scope_id: "3", status: "2", message_text: dataMessageForward![i]["message_text"] as? String ?? "", credential: "0", attachment_flag: dataMessageForward![i]["attachment_flag"] as? String ?? "", ex_blog_id: "", message_large_text: "", ex_format: "", image_id: dataMessageForward![i]["image_id"] as? String ?? "", audio_id: dataMessageForward![i]["audio_id"] as? String ?? "", video_id: dataMessageForward![i]["video_id"] as? String ?? "", file_id: dataMessageForward![i]["file_id"] as? String ?? "", thumb_id: dataMessageForward![i]["thumb_id"] as? String ?? "", reff_id: "", read_receipts: dataMessageForward![i]["read_receipts"] as? String ?? "", chat_id: "", is_call_center: "0", call_center_id: "", viewController: self, gif_id: dataMessageForward![i][TypeDataMessage.gif_id] as? String ?? "", is_forwarded: isForwarded + 1, is_secret: (dataMessageForward![i][TypeDataMessage.is_secret] as? Int) ?? 0)
  244. }
  245. dataMessageForward = nil
  246. }
  247. if isContactCenter && !isRequestContactCenter && !onGoingCC {
  248. var companyName = ""
  249. Database.shared.database?.inTransaction({ fmdb, rollback in
  250. do {
  251. if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "SELECT first_name, last_name FROM BUDDY where official_account = '1'"), cursor.next() {
  252. companyName = cursor.string(forColumnIndex: 0)! + " " + cursor.string(forColumnIndex: 1)!
  253. companyName = companyName.trimmingCharacters(in: .whitespaces)
  254. cursor.close()
  255. }
  256. } catch {
  257. rollback.pointee = true
  258. print("Access database error: \(error.localizedDescription)")
  259. }
  260. })
  261. self.dateStartCC = "\(Date().currentTimeMillis())"
  262. let myName = User.getData(pin: User.getMyPin() as String?)
  263. sendChat(message_text: "Hi \(dataPerson["name"]!!), thank you for contacting \(companyName). My name is \(myName!.fullName.trimmingCharacters(in: .whitespaces)), how can I help you?".localized(), ex_format: "1", is_call_center: "1", call_center_id: complaintId, viewController: self, isAutoSendCC: true)
  264. if channelContactCenter == "1" {
  265. if let pin = dataPerson["f_pin"] {
  266. let controller = QmeraAudioViewController()
  267. controller.user = User.getData(pin: pin)
  268. controller.isOutgoing = true
  269. controller.modalPresentationStyle = .overCurrentContext
  270. present(controller, animated: true, completion: nil)
  271. }
  272. } else if channelContactCenter == "2" {
  273. let videoVC = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "videoVCQmera") as! QmeraVideoViewController
  274. videoVC.dataPerson.append(dataPerson)
  275. self.show(videoVC, sender: nil)
  276. }
  277. }
  278. if isDirectCC {
  279. directCC()
  280. }
  281. // else if isContactCenter {
  282. // let buttonId = UIButton()
  283. // if channelContactCenter == "0" {
  284. // buttonId.tag = 0
  285. // ccAction(sender: buttonId)
  286. // } else if channelContactCenter == "1" {
  287. // buttonId.tag = 1
  288. // ccAction(sender: buttonId)
  289. // } else if channelContactCenter == "2" {
  290. // buttonId.tag = 2
  291. // ccAction(sender: buttonId)
  292. // }
  293. // }
  294. }
  295. public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
  296. guard let location = locations.last else { return }
  297. latitude = "\(location.coordinate.latitude)"
  298. longitude = "\(location.coordinate.longitude)"
  299. locationManager.stopUpdatingLocation()
  300. }
  301. public func afterUnfriend() {
  302. DispatchQueue.main.async {
  303. for timer in self.timerCredential.values {
  304. timer.invalidate()
  305. }
  306. self.timeoutCC.invalidate()
  307. SecureUserDefaults.shared.removeValue(forKey: "inEditorPersonal")
  308. NotificationCenter.default.removeObserver(self)
  309. }
  310. }
  311. private func setRightButtonItem() {
  312. navigationItem.rightBarButtonItems = nil
  313. let actionDelete = UIAction(title: "Delete Conversation".localized(), handler: {(_) in
  314. if !self.isContactCenter {
  315. let alert = LibAlertController(title: "", message: "Are you sure to delete all message in this conversation?".localized(), preferredStyle: .alert)
  316. alert.addAction(UIAlertAction(title: "Cancel".localized(), style: UIAlertAction.Style.default, handler: nil))
  317. alert.addAction(UIAlertAction(title: "Delete".localized(), style: .destructive, handler: {(_) in
  318. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  319. do {
  320. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "MESSAGE", _where: "(f_pin='\(self.dataPerson["f_pin"]!!)' or l_pin='\(self.dataPerson["f_pin"]!!)') and (message_scope_id='3' or message_scope_id='18') and is_call_center = 0")
  321. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "MESSAGE_SUMMARY", _where: "l_pin='\(self.dataPerson["f_pin"]!!)'")
  322. let l_pin = self.dataPerson["f_pin"]!!
  323. SecureUserDefaults.shared.removeValue(forKey: "saved_\(l_pin)")
  324. NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
  325. if self.fromNotification {
  326. self.didTapExit()
  327. } else {
  328. self.navigationController?.popViewController(animated: true)
  329. }
  330. } catch {
  331. rollback.pointee = true
  332. print("Access database error: \(error.localizedDescription)")
  333. }
  334. })
  335. }))
  336. self.present(alert, animated: true, completion: nil)
  337. }
  338. })
  339. let actionSearch = UIAction(title: "Search".localized(), handler: {(_) in
  340. self.isSearching = true
  341. if self.reffId != nil {
  342. self.deleteReplyView()
  343. }
  344. DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
  345. let cancelButton = UIBarButtonItem(title: "Cancel".localized(), style: .plain, target: self, action: #selector(self.cancelAction))
  346. cancelButton.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16)], for: .normal)
  347. if self.dataPerson["f_pin"] != "-999" && !self.isContactCenter {
  348. self.navigationItem.rightBarButtonItems = nil
  349. }
  350. self.navigationItem.rightBarButtonItem = cancelButton
  351. if self.isContactCenter || self.fromNotification {
  352. self.navigationItem.leftBarButtonItem = nil
  353. }
  354. self.changeAppBar()
  355. self.addMultipleSelectSession()
  356. }
  357. })
  358. let actionUnblock = UIAction(title: "Unblock".localized(), handler: {(_) in
  359. if !self.isContactCenter {
  360. DispatchQueue.global().async {
  361. if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getUnBlock(l_pin: self.dataPerson["f_pin"]!!)) {
  362. if !response.isOk() {
  363. DispatchQueue.main.async {
  364. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  365. imageView.tintColor = .white
  366. let banner = FloatingNotificationBanner(title: "Unable to complete action".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
  367. banner.show()
  368. }
  369. } else {
  370. DispatchQueue.main.async { [self] in
  371. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  372. do {
  373. _ = Database.shared.updateRecord(fmdb: fmdb, table: "BUDDY", cvalues: [
  374. "ex_block" : "0"
  375. ], _where: "f_pin = '\(self.dataPerson["f_pin"]!!)'")
  376. } catch {
  377. rollback.pointee = true
  378. print("Access database error: \(error.localizedDescription)")
  379. }
  380. })
  381. containerAction.subviews.forEach({ $0.removeFromSuperview() })
  382. containerAction.removeFromSuperview()
  383. setRightButtonItem()
  384. changeAppBar()
  385. }
  386. }
  387. } else {
  388. DispatchQueue.main.async {
  389. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  390. imageView.tintColor = .white
  391. let banner = FloatingNotificationBanner(title: "Unable to access servers".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
  392. banner.show()
  393. }
  394. }
  395. }
  396. }
  397. })
  398. let actionBlock = UIAction(title: "Block".localized(), handler: {(_) in
  399. if !self.isContactCenter {
  400. DispatchQueue.global().async {
  401. if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getBlock(l_pin: self.dataPerson["f_pin"]!!)) {
  402. if !response.isOk() {
  403. DispatchQueue.main.async {
  404. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  405. imageView.tintColor = .white
  406. let banner = FloatingNotificationBanner(title: "Unable to complete action".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
  407. banner.show()
  408. }
  409. } else {
  410. DispatchQueue.main.async { [self] in
  411. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  412. do {
  413. _ = Database.shared.updateRecord(fmdb: fmdb, table: "BUDDY", cvalues: [
  414. "ex_block" : "1"
  415. ], _where: "f_pin = '\(self.dataPerson["f_pin"]!!)'")
  416. } catch {
  417. rollback.pointee = true
  418. print("Access database error: \(error.localizedDescription)")
  419. }
  420. })
  421. setRightButtonItem()
  422. changeAppBar()
  423. }
  424. }
  425. } else {
  426. DispatchQueue.main.async {
  427. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  428. imageView.tintColor = .white
  429. let banner = FloatingNotificationBanner(title: "Unable to access servers".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
  430. banner.show()
  431. }
  432. }
  433. }
  434. }
  435. })
  436. var menu = UIMenu(title: "", children: [
  437. actionSearch,
  438. actionDelete
  439. ])
  440. let exblock = User.getDataCanNil(pin: self.dataPerson["f_pin"]!!)?.ex_block
  441. blocking = exblock == nil ? "0" : exblock!.isEmpty ? "0" : exblock!
  442. if blocking == "1" && self.dataPerson["f_pin"]!! != "-999" {
  443. menu = UIMenu(title: "", children: [
  444. actionSearch,
  445. actionUnblock,
  446. actionDelete
  447. ])
  448. blockedView(blocked: "1")
  449. } else if blocking == "0" {
  450. if self.dataPerson["f_pin"]!! != "-999" && complaintId.isEmpty {
  451. menu = UIMenu(title: "", children: [
  452. actionSearch,
  453. actionBlock,
  454. actionDelete
  455. ])
  456. } else if !complaintId.isEmpty{
  457. menu = UIMenu(title: "", children: [
  458. actionSearch
  459. ])
  460. }
  461. else {
  462. menu = UIMenu(title: "", children: [
  463. actionSearch,
  464. actionDelete
  465. ])
  466. }
  467. if containerAction.isDescendant(of: self.view) {
  468. containerAction.subviews.forEach({ $0.removeFromSuperview() })
  469. containerAction.removeFromSuperview()
  470. }
  471. } else {
  472. blockedView(blocked: "-1")
  473. changeAppBar()
  474. }
  475. let moreIcon = UIBarButtonItem(image: UIImage(systemName: "ellipsis", withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .regular, scale: .default)), menu: menu)
  476. let buttonAudioCall = UIBarButtonItem(image: UIImage(systemName: "phone", withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .regular, scale: .default)), style: .plain, target: self, action: #selector(audioVideoCall(sender:)))
  477. // let buttonSearch = UIBarButtonItem(image: UIImage(systemName: "magnifyingglass", withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .regular, scale: .default)), style: .plain, target: self, action: #selector(audioVideoCall(sender:)))
  478. buttonAudioCall.tag = 0
  479. let buttonVideoCall = UIBarButtonItem(image: UIImage(systemName: "video", withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .regular, scale: .default)), style: .plain, target: self, action: #selector(audioVideoCall(sender:)))
  480. buttonVideoCall.tag = 1
  481. let buttonAddRoom = UIBarButtonItem(image: UIImage(systemName: "plus.message", withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .regular, scale: .default)), style: .plain, target: self, action: #selector(addRoom(sender:)))
  482. if dataPerson["f_pin"] != "-999" && !isContactCenter && blocking == "0" {
  483. navigationItem.rightBarButtonItems = [moreIcon,buttonAudioCall,buttonVideoCall]
  484. } else if !isContactCenter {
  485. navigationItem.rightBarButtonItem = moreIcon
  486. } else if !complaintId.isEmpty { //!complaintId.isEmpty
  487. navigationItem.rightBarButtonItems = [moreIcon,buttonAddRoom]
  488. }
  489. }
  490. func loadData() {
  491. if (unique_l_pin != "" || isContactCenter) {
  492. getDataProfile(fPin: unique_l_pin)
  493. if isContactCenter && !isRequestContactCenter && users.count == 0 {
  494. if !unique_l_pin.isEmpty {
  495. users.append(User.getData(pin: unique_l_pin) ?? User(pin: ""))
  496. }
  497. }
  498. }
  499. if onGoingCC {
  500. SecureUserDefaults.shared.set(self.fPinContacCenter, forKey: "inEditorPersonal")
  501. } else {
  502. SecureUserDefaults.shared.set(dataPerson["f_pin"]!, forKey: "inEditorPersonal")
  503. }
  504. UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [dataPerson["f_pin"]!!])
  505. if isContactCenter || fromNotification {
  506. let imageButton = UIImageView(frame: CGRect(x: -16, y: 0, width: 20, height: 44))
  507. imageButton.image = UIImage(systemName: "chevron.backward", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .regular, scale: .default))?.withTintColor(.white)
  508. imageButton.contentMode = .left
  509. let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(didTapExit))
  510. imageButton.isUserInteractionEnabled = true
  511. imageButton.addGestureRecognizer(tapGestureRecognizer)
  512. let leftItem = UIBarButtonItem(customView: imageButton)
  513. self.navigationItem.leftBarButtonItem = leftItem
  514. }
  515. if dataPerson["f_pin"] == "-999" {
  516. chatbot()
  517. }
  518. let exblock = User.getData(pin: self.dataPerson["f_pin"]!!)?.ex_block
  519. blocking = exblock == nil ? "0" : exblock!.isEmpty ? "0" : exblock!
  520. changeAppBar()
  521. getData()
  522. tableChatView.alpha = 0
  523. tableChatView.delegate = self
  524. tableChatView.dataSource = self
  525. tableChatView.keyboardDismissMode = .interactive
  526. let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
  527. tapGesture.cancelsTouchesInView = false
  528. tableChatView.addGestureRecognizer(tapGesture)
  529. if !isContactCenter {
  530. getCounter()
  531. if counter > 0 && dataMessages.count >= counter {
  532. markerCounter = dataMessages[dataMessages.count - counter]["message_id"] as? String
  533. }
  534. tableChatView.alpha = 0
  535. if !referenceMessageId.isEmpty {
  536. if dataMessages.firstIndex(where: {$0["message_id"] as? String == referenceMessageId} ) != 0 {
  537. DispatchQueue.main.async {
  538. let section = self.dataDates.firstIndex(of: self.referenceChatDate)
  539. let row = self.dataMessages.filter({$0["chat_date"] as? String ?? "" == self.referenceChatDate}).firstIndex(where: { $0["message_id"] as? String == self.referenceMessageId})
  540. let indexPath = IndexPath(row: row!, section: section!)
  541. self.tableChatView.scrollToRow(at: indexPath, at: .middle, animated: false)
  542. self.tableChatView.cellForRow(at: indexPath)?.contentView.backgroundColor = .yellow
  543. DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
  544. self.tableChatView.cellForRow(at: indexPath)?.contentView.backgroundColor = .clear
  545. })
  546. }
  547. }
  548. } else if counter != 0 && dataMessages.count >= counter {
  549. if dataMessages.firstIndex(where: {$0["message_id"] as? String == markerCounter} ) != 0 {
  550. DispatchQueue.main.async {
  551. let data = self.dataMessages.filter({ $0["message_id"] as? String == self.markerCounter })
  552. let section = self.dataDates.firstIndex(of: data[0]["chat_date"] as? String ?? "")
  553. let row = self.dataMessages.filter({$0["chat_date"] as? String ?? "" == data[0]["chat_date"] as? String ?? ""}).firstIndex(where: { $0["message_id"] as? String == self.markerCounter})
  554. self.tableChatView.scrollToRow(at: IndexPath(row: row!, section: section!), at: .bottom, animated: false)
  555. }
  556. } else {
  557. tableChatView.scrollToTop()
  558. }
  559. DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [self] in
  560. if currentIndexpath == nil && counter != 0 {
  561. let idMe = User.getMyPin() as String?
  562. if let idx = dataMessages.firstIndex(where: { $0["message_id"] as? String == markerCounter}) {
  563. for i in idx..<dataMessages.count {
  564. if dataMessages[i]["f_pin"] as? String != idMe {
  565. sendReadMessageStatus(chat_id: "", f_pin: dataPerson["f_pin"]!!, message_scope_id: "3", message_id: dataMessages[i]["message_id"] as? String ?? "")
  566. }
  567. }
  568. counter = 0
  569. updateCounter(counter: counter)
  570. }
  571. }
  572. }
  573. } else {
  574. let l_pin = self.dataPerson["f_pin"] as? String ?? ""
  575. if let dataSaved: String = SecureUserDefaults.shared.value(forKey: "saved_\(l_pin)") {
  576. let last_m = dataSaved.components(separatedBy: ",")[0]
  577. let last_r = dataSaved.components(separatedBy: ",")[1]
  578. if !last_m.isEmpty {
  579. textFieldSend.text = last_m
  580. textFieldSend.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : UIColor.black
  581. }
  582. if !last_r.isEmpty {
  583. handleReply(indexPath: IndexPath(row: 0, section: 0), reffId: last_r)
  584. }
  585. }
  586. tableChatView.scrollToBottom(isAnimated: false)
  587. }
  588. } else if isContactCenter && onGoingCC {
  589. let idMe = User.getMyPin() as String?
  590. for i in 0..<dataMessages.count {
  591. if dataMessages[i]["f_pin"] as? String != idMe {
  592. sendReadMessageStatus(chat_id: "", f_pin: dataPerson["f_pin"]!!, message_scope_id: "3", message_id: dataMessages[i]["message_id"] as? String ?? "")
  593. }
  594. }
  595. tableChatView.scrollToBottom(isAnimated: false)
  596. } else {
  597. tableChatView.scrollToBottom(isAnimated: false)
  598. }
  599. DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
  600. if self.tableChatView.alpha != 1.0 {
  601. UIView.animate(withDuration: 0.5, animations: {
  602. self.tableChatView.alpha = 1.0
  603. })
  604. }
  605. })
  606. for data in listTimerCredential {
  607. if data.value > 0 {
  608. var second = data.value
  609. var timer = Timer()
  610. timerCredential[data.key] = timer
  611. timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: {_ in
  612. second -= 1
  613. self.listTimerCredential[data.key] = second
  614. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == data.key })
  615. if (idx != nil) {
  616. let section = self.dataDates.firstIndex(of: self.dataMessages[idx!]["chat_date"] as? String ?? "")
  617. let row = self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == self.dataMessages[idx!]["chat_date"] as? String ?? ""}).firstIndex(where: { $0["message_id"] as? String == self.dataMessages[idx!]["message_id"] as? String })
  618. if second == 0 {
  619. timer.invalidate()
  620. self.listTimerCredential.removeValue(forKey: data.key)
  621. self.timerCredential.removeValue(forKey: data.key)
  622. SecureUserDefaults.shared.removeValue(forKey: data.key)
  623. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == data.key})
  624. if idx != nil {
  625. self.dataMessages[idx!]["lock"] = "2"
  626. self.dataMessages[idx!]["reff_id"] = ""
  627. }
  628. DispatchQueue.global().async {
  629. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  630. do {
  631. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  632. "lock" : "2"
  633. ], _where: "message_id = '\(data.key)'")
  634. } catch {
  635. rollback.pointee = true
  636. print("Access database error: \(error.localizedDescription)")
  637. }
  638. })
  639. }
  640. }
  641. if row != nil && section != nil {
  642. self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
  643. }
  644. }
  645. })
  646. }
  647. }
  648. }
  649. private func chatbot() {
  650. let containerChatbot = UIView()
  651. self.view.addSubview(containerChatbot)
  652. containerChatbot.translatesAutoresizingMaskIntoConstraints = false
  653. NSLayoutConstraint.activate([
  654. containerChatbot.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
  655. containerChatbot.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
  656. containerChatbot.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
  657. containerChatbot.heightAnchor.constraint(equalToConstant: 120)
  658. ])
  659. containerChatbot.backgroundColor = .secondaryColor.withAlphaComponent(0.8)
  660. let labelChatbot = UILabel()
  661. containerChatbot.addSubview(labelChatbot)
  662. labelChatbot.translatesAutoresizingMaskIntoConstraints = false
  663. NSLayoutConstraint.activate([
  664. labelChatbot.centerYAnchor.constraint(equalTo: containerChatbot.centerYAnchor),
  665. labelChatbot.centerXAnchor.constraint(equalTo: containerChatbot.centerXAnchor),
  666. ])
  667. labelChatbot.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
  668. labelChatbot.font = UIFont.systemFont(ofSize: 12).bold
  669. labelChatbot.text = "Interactive chatbot. Coming soon".localized()
  670. }
  671. private func changeAppBar() {
  672. let viewAppBar = UIView()
  673. viewAppBar.frame.size = CGSize(width: self.view.frame.size.width, height: 44)
  674. if !isSearching {
  675. let imageProfile = UIImageView(frame: CGRect(x: 0, y: 7, width: 30, height: 30))
  676. imageProfile.circle()
  677. imageProfile.clipsToBounds = true
  678. let pictureImage = dataPerson["picture"]!
  679. var count = 0
  680. if isContactCenter {
  681. if fPinContacCenter.isEmpty && isRequestContactCenter {
  682. getImage(name: dataPerson["picture"]!!, placeholderImage: UIImage(systemName: "person.circle.fill")!) { result, isDownloaded, image in
  683. imageProfile.image = image
  684. }
  685. viewAppBar.addSubview(imageProfile)
  686. } else {
  687. if users.count == 1 {
  688. viewAppBar.addSubview(imageProfile)
  689. getImage(name: users[0].thumb, placeholderImage: UIImage(systemName: "person.circle.fill")!) { result, isDownloaded, image in
  690. imageProfile.image = image
  691. imageProfile.contentMode = .scaleAspectFit
  692. }
  693. } else {
  694. for user in users {
  695. if count == 3 {
  696. count += 1
  697. continue
  698. }
  699. if count == 0 {
  700. let pictures = UIImageView(frame: CGRect(x: 0, y: 7, width: 30, height: 30))
  701. pictures.circle()
  702. pictures.clipsToBounds = true
  703. viewAppBar.addSubview(pictures)
  704. getImage(name: user.thumb, placeholderImage: UIImage(systemName: "person.circle.fill")!) { result, isDownloaded, image in
  705. pictures.image = image
  706. pictures.contentMode = .scaleAspectFit
  707. }
  708. } else {
  709. let pictures = UIImageView(frame: CGRect(x: count * 20 , y: 7, width: 30, height: 30))
  710. pictures.circle()
  711. pictures.clipsToBounds = true
  712. viewAppBar.addSubview(pictures)
  713. getImage(name: user.thumb, placeholderImage: UIImage(systemName: "person.circle.fill")!) { result, isDownloaded, image in
  714. pictures.image = image
  715. pictures.contentMode = .scaleAspectFit
  716. }
  717. }
  718. count += 1
  719. }
  720. }
  721. }
  722. } else if dataPerson["f_pin"]!! == "-999" {
  723. viewAppBar.addSubview(imageProfile)
  724. if !Utils.getIconDock().isEmpty {
  725. let urlString = Utils.getUrlDock()!
  726. if let cachedImage = ImageCache.shared.image(forKey: urlString) {
  727. let imageData = cachedImage
  728. imageProfile.image = imageData
  729. } else {
  730. DispatchQueue.global().async{
  731. Utils.fetchDataWithCookiesAndUserAgent(from: URL(string: urlString)!) { data, response, error in
  732. guard let data = data, error == nil else { return }
  733. DispatchQueue.main.async() {
  734. if UIImage(data: data) != nil {
  735. let imageData = UIImage(data: data)!
  736. imageProfile.image = imageData
  737. ImageCache.shared.save(image: imageData, forKey: urlString)
  738. }
  739. }
  740. }
  741. }
  742. }
  743. } else {
  744. imageProfile.image = UIImage(named: "pb_button", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  745. }
  746. imageProfile.contentMode = .scaleAspectFit
  747. }
  748. else if (pictureImage != "" && pictureImage != nil) {
  749. viewAppBar.addSubview(imageProfile)
  750. imageProfile.setImage(name: pictureImage!)
  751. imageProfile.contentMode = .scaleAspectFill
  752. } else {
  753. viewAppBar.addSubview(imageProfile)
  754. imageProfile.image = UIImage(systemName: "person")
  755. imageProfile.contentMode = .scaleAspectFit
  756. imageProfile.backgroundColor = .lightGray
  757. }
  758. var titleNavigation = UILabel(frame: CGRect(x: 35, y: 0, width: viewAppBar.frame.size.width - 250, height: 44))
  759. if blocking == "-1" || blocking == "1" {
  760. titleNavigation = UILabel(frame: CGRect(x: 35, y: 0, width: viewAppBar.frame.size.width - 150, height: 44))
  761. } else if isContactCenter {
  762. titleNavigation = UILabel(frame: CGRect(x: 35, y: 0, width: viewAppBar.frame.size.width - 150, height: 44))
  763. if users.count > 0 {
  764. titleNavigation = UILabel(frame: CGRect(x: 35 * (CGFloat(users.count)) - (CGFloat((users.count - 1) * 15)), y: 0, width: viewAppBar.frame.size.width - 150 - (35 * (CGFloat(users.count - 1)) - (CGFloat((users.count - 1) * 15))), height: 44))
  765. }
  766. }
  767. viewAppBar.addSubview(titleNavigation)
  768. if ((User.isOfficial(official_account: (dataPerson["isOfficial"] ?? "")!) || User.isOfficialRegular(official_account: (dataPerson["isOfficial"] ?? "")!)) && !isContactCenter) || ((User.isOfficial(official_account: (dataPerson["isOfficial"] ?? "")!) || User.isOfficialRegular(official_account: (dataPerson["isOfficial"] ?? "")!)) && fPinContacCenter.isEmpty) {
  769. var name = dataPerson["name"]!!
  770. if (isContactCenter) {
  771. name = name + " " + "Contact Center".localized()
  772. titleNavigation.text = name
  773. } else {
  774. titleNavigation.set(image: UIImage(named: "ic_official_flag", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, with: " \(name)", size: 15, y: -4)
  775. }
  776. } else if User.isVerified(official_account: (dataPerson["isOfficial"] ?? "")!) && !isContactCenter {
  777. let name = dataPerson["name"]!!
  778. titleNavigation.set(image: UIImage(named: "ic_verified", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, with: " \(name)", size: 15, y: -4)
  779. } else if User.isInternal(userType: (dataPerson["user_type"] ?? "")!) && !isContactCenter {
  780. let name = dataPerson["name"]!!
  781. titleNavigation.set(image: UIImage(named: "ic_internal", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, with: " \(name)", size: 15, y: -4)
  782. } else {
  783. if !isContactCenter {
  784. titleNavigation.text = dataPerson["name"] as? String
  785. } else {
  786. if users.count == 1 {
  787. titleNavigation.text = users[0].fullName
  788. } else {
  789. var stringName = ""
  790. for user in users {
  791. if stringName.isEmpty {
  792. stringName = user.fullName
  793. } else {
  794. stringName += ", \(user.fullName)"
  795. }
  796. }
  797. titleNavigation.text = stringName
  798. }
  799. }
  800. }
  801. titleNavigation.textColor = .white
  802. titleNavigation.font = UIFont.systemFont(ofSize: 12).bold
  803. navigationItem.titleView = viewAppBar
  804. titleText = titleNavigation.text
  805. } else {
  806. searchBar = UISearchBar()
  807. searchBar.autocapitalizationType = .none
  808. searchBar.delegate = self
  809. searchBar.searchTextField.tintColor = .mainColor
  810. searchBar.searchTextField.textColor = .mainColor
  811. searchBar.showsCancelButton = false
  812. // searchBar.setMagnifyingGlassColorTo(color: .white)
  813. // searchBar.updateHeight(height: 36, radius: 18)
  814. searchBar.setImage(UIImage(), for: .search, state: .normal)
  815. searchBar.setPositionAdjustment(UIOffset(horizontal: 10, vertical: 0), for: .search)
  816. searchBar.setCustomBackgroundImage(image: UIImage(named: self.traitCollection.userInterfaceStyle == .dark ? "nx_search_bar_dark" : "nx_search_bar", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!)
  817. navigationItem.titleView = searchBar
  818. self.definesPresentationContext = true
  819. }
  820. if copySession || forwardSession || deleteSession || isSearching {
  821. navigationItem.hidesBackButton = true
  822. navigationController?.interactivePopGestureRecognizer?.isEnabled = false
  823. } else {
  824. navigationItem.hidesBackButton = false
  825. navigationController?.interactivePopGestureRecognizer?.isEnabled = true
  826. }
  827. viewAppBar.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(seeProfileTapped)))
  828. }
  829. private func getDataProfile(fPin: String) {
  830. var query = "SELECT f_pin, first_name || ' ' || last_name, official_account, image_id, device_id, offline_mode, user_type FROM BUDDY where f_pin = '\(fPin)'"
  831. if (isContactCenter && isRequestContactCenter) {
  832. query = "SELECT group_id, f_name, official, image_id FROM GROUPZ where group_type = 1 AND official = 1"
  833. }
  834. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  835. do {
  836. if let cursorData = Database.shared.getRecords(fmdb: fmdb, query: query) {
  837. if cursorData.next() {
  838. dataPerson["f_pin"] = cursorData.string(forColumnIndex: 0)
  839. dataPerson["name"] = cursorData.string(forColumnIndex: 1)?.trimmingCharacters(in: .whitespaces)
  840. dataPerson["picture"] = cursorData.string(forColumnIndex: 3)
  841. dataPerson["isOfficial"] = cursorData.string(forColumnIndex: 2)
  842. if isContactCenter && isRequestContactCenter {
  843. dataPerson["user_type"] = "0"
  844. } else {
  845. dataPerson["user_type"] = cursorData.string(forColumnIndex: 6)
  846. }
  847. } else {
  848. dataPerson["f_pin"] = "-999"
  849. dataPerson["name"] = "Bot"
  850. dataPerson["picture"] = ""
  851. dataPerson["isOfficial"] = ""
  852. dataPerson["deviceId"] = ""
  853. dataPerson["isOffline"] = "0"
  854. dataPerson["user_type"] = "0"
  855. }
  856. cursorData.close()
  857. }
  858. } catch {
  859. rollback.pointee = true
  860. print("Access database error: \(error.localizedDescription)")
  861. }
  862. })
  863. }
  864. private func addDataMessage() {
  865. multipleOffsetUp += 1
  866. let queryCount = "SELECT COUNT(*) FROM MESSAGE where (f_pin='\(dataPerson["f_pin"]!!)' or l_pin='\(dataPerson["f_pin"]!!)') AND (message_scope_id = '3' OR message_scope_id = '18') AND is_call_center = 0"
  867. let query = "SELECT message_id, f_pin, l_pin, message_scope_id, server_date, status, message_text, audio_id, video_id, image_id, thumb_id, read_receipts, chat_id, file_id, attachment_flag, reff_id, lock, is_stared, blog_id, credential, is_call_center, call_center_id, opposite_pin, last_edited, gif_id, is_forwarded_message FROM MESSAGE where (f_pin='\(dataPerson["f_pin"]!!)' or l_pin='\(dataPerson["f_pin"]!!)') AND (message_scope_id = '3' OR message_scope_id = '18') AND is_call_center = 0 order by server_date asc LIMIT CASE WHEN (\(queryCount))-\(dataMessages.count)>=20 THEN 20*\(multipleOffsetUp-1) ELSE (\(queryCount))-\(dataMessages.count) END OFFSET CASE WHEN (\(queryCount))>=\(20*multipleOffsetUp) THEN (\(queryCount))-\(20*multipleOffsetUp) ELSE 0 END"
  868. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  869. do {
  870. if let cursorData = Database.shared.getRecords(fmdb: fmdb, query: query) {
  871. var tempData: [[String: Any?]] = []
  872. while cursorData.next() {
  873. var row: [String: Any?] = [:]
  874. row["message_id"] = cursorData.string(forColumnIndex: 0)
  875. row["f_pin"] = cursorData.string(forColumnIndex: 1)
  876. row["l_pin"] = cursorData.string(forColumnIndex: 2)
  877. row["message_scope_id"] = cursorData.string(forColumnIndex: 3)
  878. row["server_date"] = cursorData.string(forColumnIndex: 4)
  879. row["status"] = cursorData.string(forColumnIndex: 5)
  880. row["message_text"] = cursorData.string(forColumnIndex: 6)
  881. row["audio_id"] = cursorData.string(forColumnIndex: 7)
  882. row["video_id"] = cursorData.string(forColumnIndex: 8)
  883. row["image_id"] = cursorData.string(forColumnIndex: 9)
  884. row["thumb_id"] = cursorData.string(forColumnIndex: 10)
  885. row["read_receipts"] = cursorData.string(forColumnIndex: 11)
  886. row["chat_id"] = cursorData.string(forColumnIndex: 12)
  887. row["file_id"] = cursorData.string(forColumnIndex: 13)
  888. row["attachment_flag"] = cursorData.string(forColumnIndex: 14)
  889. row["reff_id"] = cursorData.string(forColumnIndex: 15)
  890. row["lock"] = cursorData.string(forColumnIndex: 16)
  891. row["is_stared"] = cursorData.string(forColumnIndex: 17)
  892. row["blog_id"] = cursorData.string(forColumnIndex: 18)
  893. row["credential"] = cursorData.string(forColumnIndex: 19)
  894. row[TypeDataMessage.is_call_center] = cursorData.string(forColumnIndex: 20)
  895. row[TypeDataMessage.call_center_id] = cursorData.string(forColumnIndex: 21)
  896. row[TypeDataMessage.opposite_pin] = cursorData.string(forColumnIndex: 22)
  897. row[TypeDataMessage.last_edit] = cursorData.longLongInt(forColumnIndex: 23)
  898. row[TypeDataMessage.gif_id] = cursorData.string(forColumnIndex: 24)
  899. row[TypeDataMessage.is_forwarded] = Int(cursorData.int(forColumnIndex: 25))
  900. if let cursorStatus = Database.shared.getRecords(fmdb: fmdb, query: "SELECT status FROM MESSAGE_STATUS WHERE message_id='\(row["message_id"] as? String ?? "")'") {
  901. while cursorStatus.next() {
  902. row["status"] = cursorStatus.string(forColumnIndex: 0)
  903. }
  904. cursorStatus.close()
  905. }
  906. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  907. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  908. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  909. if let dirPath = paths.first {
  910. let videoURL = URL(fileURLWithPath: dirPath).appendingPathComponent(row["video_id"] as? String ?? "")
  911. let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(row["file_id"] as? String ?? "")
  912. if ((row["video_id"] as? String ?? "") != "") {
  913. if FileManager.default.fileExists(atPath: videoURL.path) || FileEncryption.shared.isSecureExists(filename: videoURL.lastPathComponent){
  914. row["progress"] = 100.0
  915. } else {
  916. row["progress"] = 0.0
  917. }
  918. } else {
  919. if FileManager.default.fileExists(atPath: fileURL.path) || FileEncryption.shared.isSecureExists(filename: fileURL.lastPathComponent){
  920. row["progress"] = 100.0
  921. } else {
  922. row["progress"] = 0.0
  923. }
  924. }
  925. }
  926. row["chat_date"] = chatDate(stringDate: row["server_date"] as? String ?? "")
  927. row["isSelected"] = false
  928. if row["credential"] != nil && row["credential"] as? String ?? "" == "1" {
  929. let idMe = User.getMyPin()!
  930. if row["f_pin"] as? String ?? "" == idMe {
  931. let second = getSecondsDifferenceFromTwoDates(start: Date.init(milliseconds: Int64(row["server_date"] as? String ?? "")!), end: Date())
  932. if second > 60 {
  933. listTimerCredential[row["message_id"] as? String ?? ""] = 0
  934. row["lock"] = "2"
  935. row["reff_id"] = ""
  936. DispatchQueue.global().async {
  937. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  938. do {
  939. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  940. "lock" : "2"
  941. ], _where: "message_id = '\(row["message_id"] as? String ?? "")'")
  942. } catch {
  943. rollback.pointee = true
  944. print("Access database error: \(error.localizedDescription)")
  945. }
  946. })
  947. }
  948. } else {
  949. let second = 60 - second
  950. listTimerCredential[row["message_id"] as? String ?? ""] = second
  951. }
  952. } else {
  953. let hasMessageId: String? = SecureUserDefaults.shared.value(forKey: row["message_id"] as? String ?? "") ?? nil
  954. if hasMessageId != nil {
  955. let second = getSecondsDifferenceFromTwoDates(start: Date.init(milliseconds: Int64(hasMessageId!)!), end: Date())
  956. if second > 60 {
  957. listTimerCredential[row["message_id"] as? String ?? ""] = 0
  958. row["lock"] = "2"
  959. row["reff_id"] = ""
  960. DispatchQueue.global().async {
  961. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  962. do {
  963. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  964. "lock" : "2"
  965. ], _where: "message_id = '\(row["message_id"] as? String ?? "")'")
  966. } catch {
  967. rollback.pointee = true
  968. print("Access database error: \(error.localizedDescription)")
  969. }
  970. })
  971. }
  972. } else {
  973. let second = 60 - second
  974. listTimerCredential[row["message_id"] as? String ?? ""] = second
  975. }
  976. } else {
  977. SecureUserDefaults.shared.set("\(Date().currentTimeMillis())", forKey: row["message_id"] as? String ?? "")
  978. listTimerCredential[row["message_id"] as? String ?? ""] = 60
  979. }
  980. }
  981. }
  982. tempData.append(row)
  983. }
  984. if tempData.count != 0 && (dataMessages.firstIndex(where: { $0["message_id"] as? String == tempData[0]["message_id"] as? String }) == nil) {
  985. let lastIndex = tempData.count - 1
  986. for i in 0..<tempData.count {
  987. tableChatView.beginUpdates()
  988. dataMessages.insert(tempData[lastIndex - i], at: 0)
  989. if dataMessages.firstIndex(where: { $0["chat_date"] as? String == tempData[lastIndex - i]["chat_date"] as? String }) != nil {
  990. tableChatView.insertRows(at: [IndexPath(row: 0, section: currentIndexpath!.section)], with: .top)
  991. } else {
  992. tableChatView.insertSections(IndexSet(integer: 0), with: .top)
  993. tableChatView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .top)
  994. }
  995. tableChatView.endUpdates()
  996. }
  997. }
  998. cursorData.close()
  999. gettingDataMessage = false
  1000. }
  1001. } catch {
  1002. rollback.pointee = true
  1003. print("Access database error: \(error.localizedDescription)")
  1004. }
  1005. })
  1006. }
  1007. private func getData() {
  1008. // let queryCount = "SELECT COUNT(*) FROM MESSAGE where (f_pin='\(dataPerson["f_pin"]!!)' or l_pin='\(dataPerson["f_pin"]!!)') AND (message_scope_id = '3' OR message_scope_id = '18') AND is_call_center = 0"
  1009. // var query = "SELECT message_id, f_pin, l_pin, message_scope_id, server_date, status, message_text, audio_id, video_id, image_id, thumb_id, read_receipts, chat_id, file_id, attachment_flag, reff_id, lock, is_stared, blog_id, credential FROM MESSAGE where (f_pin='\(dataPerson["f_pin"]!!)' or l_pin='\(dataPerson["f_pin"]!!)') AND (message_scope_id = '3' OR message_scope_id = '18') AND is_call_center = 0 order by server_date asc LIMIT CASE WHEN (\(queryCount))-\(dataMessages.count)>=20 THEN 20 ELSE (\(queryCount))-\(dataMessages.count) END OFFSET CASE WHEN (\(queryCount))>=\(20*multipleOffsetUp) THEN (\(queryCount))-\(20*multipleOffsetUp) ELSE 0 END"
  1010. var query = "SELECT message_id, f_pin, l_pin, message_scope_id, server_date, status, message_text, audio_id, video_id, image_id, thumb_id, read_receipts, chat_id, file_id, attachment_flag, reff_id, lock, is_stared, blog_id, credential, is_call_center, call_center_id, opposite_pin, last_edited, gif_id, is_forwarded_message FROM MESSAGE where (f_pin='\(dataPerson["f_pin"]!!)' or l_pin='\(dataPerson["f_pin"]!!)') AND (message_scope_id = '3' OR message_scope_id = '18') AND is_call_center = 0 order by server_date asc"
  1011. if isContactCenter {
  1012. if complaintId.isEmpty {
  1013. query = "SELECT message_id, f_pin, l_pin, message_scope_id, server_date, status, message_text, audio_id, video_id, image_id, thumb_id, read_receipts, chat_id, file_id, attachment_flag, reff_id, lock, is_stared FROM MESSAGE where (f_pin='\(dataPerson["f_pin"]!!)' or l_pin='\(dataPerson["f_pin"]!!)') AND message_scope_id = '5' AND broadcast_flag = 0 AND is_call_center = 1 order by server_date asc"
  1014. } else {
  1015. query = "SELECT message_id, f_pin, l_pin, message_scope_id, server_date, status, message_text, audio_id, video_id, image_id, thumb_id, read_receipts, chat_id, file_id, attachment_flag, reff_id, lock, is_stared FROM MESSAGE where message_scope_id = '5' AND broadcast_flag = 0 AND is_call_center = 1 AND call_center_id = '\(complaintId)' order by server_date asc"
  1016. }
  1017. if isRequestContactCenter && !isDirectCC {
  1018. viewButton.isHidden = true
  1019. viewTextfield.isHidden = true
  1020. var row: [String: Any?] = [:]
  1021. row["f_pin"] = nil
  1022. row["message_id"] = ""
  1023. row["chat_date"] = "Today".localized()
  1024. let listStringName: [String] = ["Messaging".localized(), "Secure SMS".localized(), "VoIP Call".localized(), "Email".localized(), "Video Call".localized(), "GSM Call".localized(), "GPT Chatbot".localized(), "WhatsApp"]
  1025. var data : [CategoryCC] = []
  1026. let channels : [String] = ["0", "4", "1", "3", "2", "5", "7", "6"]
  1027. if Utils.getDefaultCC() == "No" {
  1028. let category = CategoryCC.getDatafromParent(parent: CategoryCC.default_parent)
  1029. for i in 0..<category.count {
  1030. data.append(CategoryCC(id: "level0_\(i)", service_id: category[i].service_id, service_name: category[i].service_name, parent: category[i].parent, description: category[i].description, is_tablet: "0"))
  1031. }
  1032. } else {
  1033. for i in 0..<listStringName.count {
  1034. data.append(CategoryCC(id: "level0_\(channels[i])", service_id: "", service_name: listStringName[i], parent: "\(i)", description: "", is_tablet: "0"))
  1035. }
  1036. row["attachment_flag"] = "503"
  1037. }
  1038. row["category_cc"] = data
  1039. dataDates.append("Today".localized())
  1040. dataMessages.append(row)
  1041. } else if isDirectCC {
  1042. dataDates.append("Today".localized())
  1043. }
  1044. }
  1045. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  1046. do {
  1047. if let cursorData = Database.shared.getRecords(fmdb: fmdb, query: query) {
  1048. var tempImages: [ImageGrouping] = []
  1049. while cursorData.next() {
  1050. var row: [String: Any?] = [:]
  1051. row["message_id"] = cursorData.string(forColumnIndex: 0)
  1052. row["f_pin"] = cursorData.string(forColumnIndex: 1)
  1053. row["l_pin"] = cursorData.string(forColumnIndex: 2)
  1054. row["message_scope_id"] = cursorData.string(forColumnIndex: 3)
  1055. row["server_date"] = cursorData.string(forColumnIndex: 4)
  1056. row["status"] = cursorData.string(forColumnIndex: 5)
  1057. row["message_text"] = cursorData.string(forColumnIndex: 6)
  1058. row["audio_id"] = cursorData.string(forColumnIndex: 7)
  1059. row["video_id"] = cursorData.string(forColumnIndex: 8)
  1060. row["image_id"] = cursorData.string(forColumnIndex: 9)
  1061. row["thumb_id"] = cursorData.string(forColumnIndex: 10)
  1062. row["read_receipts"] = cursorData.string(forColumnIndex: 11)
  1063. row["chat_id"] = cursorData.string(forColumnIndex: 12)
  1064. row["file_id"] = cursorData.string(forColumnIndex: 13)
  1065. row["attachment_flag"] = cursorData.string(forColumnIndex: 14)
  1066. row["reff_id"] = cursorData.string(forColumnIndex: 15)
  1067. row["lock"] = cursorData.string(forColumnIndex: 16)
  1068. row["is_stared"] = cursorData.string(forColumnIndex: 17)
  1069. row["blog_id"] = cursorData.string(forColumnIndex: 18)
  1070. row["credential"] = cursorData.string(forColumnIndex: 19)
  1071. row[TypeDataMessage.is_call_center] = cursorData.string(forColumnIndex: 20)
  1072. row[TypeDataMessage.call_center_id] = cursorData.string(forColumnIndex: 21)
  1073. row[TypeDataMessage.opposite_pin] = cursorData.string(forColumnIndex: 22)
  1074. row[TypeDataMessage.last_edit] = cursorData.longLongInt(forColumnIndex: 23)
  1075. row[TypeDataMessage.gif_id] = cursorData.string(forColumnIndex: 24)
  1076. row[TypeDataMessage.is_forwarded] = Int(cursorData.int(forColumnIndex: 25))
  1077. if let cursorStatus = Database.shared.getRecords(fmdb: fmdb, query: "SELECT status FROM MESSAGE_STATUS WHERE message_id='\(row["message_id"] as? String ?? "")'") {
  1078. while cursorStatus.next() {
  1079. row["status"] = cursorStatus.string(forColumnIndex: 0)
  1080. }
  1081. cursorStatus.close()
  1082. }
  1083. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  1084. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  1085. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  1086. if let dirPath = paths.first {
  1087. let videoURL = URL(fileURLWithPath: dirPath).appendingPathComponent(row["video_id"] as? String ?? "")
  1088. let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(row["file_id"] as? String ?? "")
  1089. if ((row["video_id"] as? String ?? "") != "") {
  1090. if FileManager.default.fileExists(atPath: videoURL.path) || FileEncryption.shared.isSecureExists(filename: videoURL.lastPathComponent){
  1091. row["progress"] = 100.0
  1092. } else {
  1093. row["progress"] = 0.0
  1094. }
  1095. } else {
  1096. if FileManager.default.fileExists(atPath: fileURL.path) || FileEncryption.shared.isSecureExists(filename: fileURL.lastPathComponent){
  1097. row["progress"] = 100.0
  1098. } else {
  1099. row["progress"] = 0.0
  1100. }
  1101. }
  1102. }
  1103. row["chat_date"] = chatDate(stringDate: row["server_date"] as? String ?? "")
  1104. row["isSelected"] = false
  1105. if row["credential"] != nil && row["credential"] as? String ?? "" == "1" {
  1106. let idMe = User.getMyPin()!
  1107. if row["f_pin"] as? String ?? "" == idMe {
  1108. let second = getSecondsDifferenceFromTwoDates(start: Date.init(milliseconds: Int64(row["server_date"] as? String ?? "")!), end: Date())
  1109. if second > 60 {
  1110. listTimerCredential[row["message_id"] as? String ?? ""] = 0
  1111. row["lock"] = "2"
  1112. row["reff_id"] = ""
  1113. DispatchQueue.global().async {
  1114. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  1115. do {
  1116. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  1117. "lock" : "2"
  1118. ], _where: "message_id = '\(row["message_id"] as? String ?? "")'")
  1119. } catch {
  1120. rollback.pointee = true
  1121. print("Access database error: \(error.localizedDescription)")
  1122. }
  1123. })
  1124. }
  1125. } else {
  1126. let second = 60 - second
  1127. listTimerCredential[row["message_id"] as? String ?? ""] = second
  1128. }
  1129. } else {
  1130. let hasMessageId: String? = SecureUserDefaults.shared.value(forKey: row["message_id"] as? String ?? "") ?? nil
  1131. if hasMessageId != nil {
  1132. let second = getSecondsDifferenceFromTwoDates(start: Date.init(milliseconds: Int64(hasMessageId!)!), end: Date())
  1133. if second > 60 {
  1134. listTimerCredential[row["message_id"] as? String ?? ""] = 0
  1135. row["lock"] = "2"
  1136. row["reff_id"] = ""
  1137. DispatchQueue.global().async {
  1138. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  1139. do {
  1140. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  1141. "lock" : "2"
  1142. ], _where: "message_id = '\(row["message_id"] as? String ?? "")'")
  1143. } catch {
  1144. rollback.pointee = true
  1145. print("Access database error: \(error.localizedDescription)")
  1146. }
  1147. })
  1148. }
  1149. } else {
  1150. let second = 60 - second
  1151. listTimerCredential[row["message_id"] as? String ?? ""] = second
  1152. }
  1153. } else {
  1154. SecureUserDefaults.shared.set("\(Date().currentTimeMillis())", forKey: row["message_id"] as? String ?? "")
  1155. listTimerCredential[row["message_id"] as? String ?? ""] = 60
  1156. }
  1157. }
  1158. }
  1159. if (dataMessages.count == 0 || dataMessages.last!["f_pin"] as? String ?? "" == row["f_pin"] as? String ?? "") && tempImages.count <= 30 && row["image_id"] != nil && !(row["image_id"] as? String ?? "").isEmpty && (row["message_text"] as? String ?? "").isEmpty && (row["reff_id"] as? String ?? "").isEmpty && (row["credential"] as? String ?? "") != "1" && (row["read_receipts"] as? String ?? "") != "8" {
  1160. if tempImages.count != 0 && getSecondsDifferenceFromTwoDates(start: Date.init(milliseconds: Int64(tempImages.last!.time)!), end: Date.init(milliseconds: Int64(row["server_date"] as? String ?? "")!))/60 >= 11 {
  1161. if tempImages.count >= 4 {
  1162. groupImages[tempImages[0].messageId] = tempImages
  1163. if let idxTemp = dataMessages.firstIndex(where: { $0["message_id"] as? String ?? "" == tempImages[0].messageId }) {
  1164. for _ in 1..<tempImages.count {
  1165. dataMessages.remove(at: idxTemp + 1)
  1166. }
  1167. }
  1168. }
  1169. tempImages.removeAll()
  1170. }
  1171. tempImages.append(ImageGrouping(messageId: row["message_id"] as? String ?? "", thumbId: row["thumb_id"] as? String ?? "", imageId: row["image_id"] as? String ?? "", status: row["status"] as? String ?? "", time: row["server_date"] as? String ?? "", lPin: row["l_pin"] as? String ?? "", dataMessage: row, dataPerson: dataPerson, dataGroup: [:], dataTopic: [:]))
  1172. } else if tempImages.count >= 4 {
  1173. groupImages[tempImages[0].messageId] = tempImages
  1174. if let idxTemp = dataMessages.firstIndex(where: { $0["message_id"] as? String ?? "" == tempImages[0].messageId }) {
  1175. for _ in 1..<tempImages.count {
  1176. dataMessages.remove(at: idxTemp + 1)
  1177. }
  1178. }
  1179. tempImages.removeAll()
  1180. } else if tempImages.count != 0 {
  1181. tempImages.removeAll()
  1182. }
  1183. dataMessages.append(row)
  1184. }
  1185. if tempImages.count >= 4 {
  1186. if tempImages.count > 30 {
  1187. tempImages.removeSubrange(30..<tempImages.count)
  1188. }
  1189. groupImages[tempImages[0].messageId] = tempImages
  1190. if let idxTemp = dataMessages.firstIndex(where: { $0["message_id"] as? String ?? "" == tempImages[0].messageId }) {
  1191. for _ in 1..<tempImages.count {
  1192. dataMessages.remove(at: idxTemp + 1)
  1193. }
  1194. }
  1195. }
  1196. cursorData.close()
  1197. }
  1198. } catch {
  1199. rollback.pointee = true
  1200. print("Access database error: \(error.localizedDescription)")
  1201. }
  1202. })
  1203. }
  1204. func getSecondsDifferenceFromTwoDates(start: Date, end: Date) -> Int {
  1205. let diff = Int(end.timeIntervalSince1970 - start.timeIntervalSince1970)
  1206. let hours = diff / 3600
  1207. let seconds = (diff - hours * 3600)
  1208. return seconds
  1209. }
  1210. func chatDate(stringDate: String) -> String {
  1211. let date = Date(milliseconds: Int64(stringDate)!)
  1212. let calendar = Calendar.current
  1213. if (calendar.isDateInToday(date)) {
  1214. if !dataDates.contains("Today".localized()){
  1215. dataDates.append("Today".localized())
  1216. }
  1217. return "Today".localized()
  1218. } else {
  1219. let startOfNow = calendar.startOfDay(for: Date())
  1220. let startOfTimeStamp = calendar.startOfDay(for: date)
  1221. let components = calendar.dateComponents([.day], from: startOfNow, to: startOfTimeStamp)
  1222. let day = -(components.day!)
  1223. if day == 1{
  1224. if !dataDates.contains("Yesterday".localized()){
  1225. dataDates.append("Yesterday".localized())
  1226. }
  1227. return "Yesterday".localized()
  1228. } else if day < 7 {
  1229. let formatter = DateFormatter()
  1230. formatter.dateFormat = "EEEE"
  1231. let lang: String = SecureUserDefaults.shared.value(forKey: "i18n_language") ?? "en"
  1232. if lang == "id" {
  1233. formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
  1234. }
  1235. if !dataDates.contains(formatter.string(from: date)){
  1236. dataDates.append(formatter.string(from: date))
  1237. }
  1238. return formatter.string(from: date)
  1239. } else {
  1240. let formatter = DateFormatter()
  1241. formatter.dateFormat = "EE, dd MMM"
  1242. let lang: String = SecureUserDefaults.shared.value(forKey: "i18n_language") ?? "en"
  1243. if lang == "id" {
  1244. formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
  1245. }
  1246. let stringFormat = formatter.string(from: date as Date)
  1247. if !dataDates.contains(stringFormat){
  1248. dataDates.append(stringFormat)
  1249. }
  1250. return stringFormat
  1251. }
  1252. }
  1253. }
  1254. func updateProgress(_ data: [AnyHashable : Any]){
  1255. var isImage = false
  1256. var idx = dataMessages.lastIndex(where: { $0["video_id"] as? String == data["name"] as? String || $0["video_id"] as? String == data["video_id"] as? String })
  1257. if (idx == nil) {
  1258. idx = dataMessages.lastIndex(where: { $0["image_id"] as? String == data["name"] as? String || $0["image_id"] as? String == data["image_id"] as? String })
  1259. isImage = true
  1260. }
  1261. if (idx != nil) {
  1262. let section = dataDates.firstIndex(of: dataMessages[idx!]["chat_date"] as? String ?? "")
  1263. if section == nil {
  1264. return
  1265. }
  1266. let row = dataMessages.filter({ $0["chat_date"] as? String ?? "" == dataDates[section!]}).firstIndex(where: { $0["message_id"] as? String == dataMessages[idx!]["message_id"] as? String})
  1267. if row == nil {
  1268. return
  1269. }
  1270. DispatchQueue.main.async {
  1271. let indexPath = IndexPath(row: row!, section: section!)
  1272. if(self.fakeProgMultip < self.maxFakeProgMultip){
  1273. self.fakeProgMultip = self.fakeProgMultip + 1
  1274. }
  1275. let fakeProgress = Double(self.fakeProgMultip) * (100.0 / Double(self.maxFakeProgMultip))
  1276. let progress = max(data["progress"] as! Double, fakeProgress)
  1277. if(data["progress"] as! Double == 100.0){
  1278. self.fakeProgMultip = 0
  1279. }
  1280. if let cell = self.tableChatView.cellForRow(at: indexPath) {
  1281. for view in cell.contentView.subviews {
  1282. if !(view is UITextView) && !(view is UIImageView) {
  1283. for viewInContainer in view.subviews {
  1284. if viewInContainer is UIImageView {
  1285. if viewInContainer.subviews.count == 0 {
  1286. return
  1287. }
  1288. var containerView : UIView?
  1289. if (isImage) {
  1290. containerView = viewInContainer.subviews[0]
  1291. } else if viewInContainer.subviews.count > 1 {
  1292. containerView = viewInContainer.subviews[1]
  1293. }
  1294. if let loading = containerView?.layer.sublayers?[1] as? CAShapeLayer {
  1295. loading.strokeEnd = CGFloat(progress / 100)
  1296. if (progress == 100.0) {
  1297. self.dataMessages[idx!]["progress"] = progress
  1298. self.tableChatView.reloadRows(at: [indexPath], with: .none)
  1299. }
  1300. }
  1301. }
  1302. }
  1303. }
  1304. }
  1305. }
  1306. }
  1307. } else {
  1308. idx = dataMessages.lastIndex(where: { $0["file_id"] as? String == data["name"] as? String || $0["file_id"] as? String == data["file_id"] as? String })
  1309. if (idx != nil) {
  1310. let section = dataDates.firstIndex(of: dataMessages[idx!]["chat_date"] as? String ?? "")
  1311. if section == nil {
  1312. return
  1313. }
  1314. let row = dataMessages.filter({ $0["chat_date"] as? String ?? "" == dataDates[section!]}).firstIndex(where: { $0["message_id"] as? String == dataMessages[idx!]["message_id"] as? String})
  1315. if row == nil {
  1316. return
  1317. }
  1318. DispatchQueue.main.async {
  1319. let indexPath = IndexPath(row: row!, section: section!)
  1320. if(self.fakeProgMultip < self.maxFakeProgMultip){
  1321. self.fakeProgMultip = self.fakeProgMultip + 1
  1322. }
  1323. let fakeProgress = Double(self.fakeProgMultip) * (100.0 / Double(self.maxFakeProgMultip))
  1324. let progress = max(data["progress"] as! Double, fakeProgress)
  1325. if(data["progress"] as! Double == 100.0){
  1326. self.fakeProgMultip = 0
  1327. }
  1328. if let cell = self.tableChatView.cellForRow(at: indexPath) {
  1329. for view in cell.contentView.subviews {
  1330. if !(view is UITextView) && !(view is UIImageView) {
  1331. for viewSubviews in view.subviews {
  1332. if !(viewSubviews is UITextView) {
  1333. for viewInContainer in viewSubviews.subviews {
  1334. if !(viewInContainer is UITextView) && !(viewInContainer is UIImageView) {
  1335. if let cont = viewInContainer.layer.sublayers {
  1336. if cont.count < 2 {
  1337. return
  1338. }
  1339. }
  1340. if let layers = viewInContainer.layer.sublayers {
  1341. if let loading = layers [1] as? CAShapeLayer {
  1342. loading.strokeEnd = CGFloat(progress / 100)
  1343. if (progress == 100.0) {
  1344. self.dataMessages[idx!]["progress"] = progress
  1345. self.tableChatView.reloadRows(at: [indexPath], with: .none)
  1346. }
  1347. }
  1348. }
  1349. }
  1350. }
  1351. }
  1352. }
  1353. }
  1354. }
  1355. }
  1356. }
  1357. }
  1358. }
  1359. }
  1360. @objc func onUploadChat(notification: NSNotification) {
  1361. let data:[AnyHashable : Any] = notification.userInfo!
  1362. updateProgress(data)
  1363. }
  1364. @objc func onReceiveMessage(notification: NSNotification) {
  1365. DispatchQueue.main.async { [self] in
  1366. let data:[AnyHashable : Any] = notification.userInfo!
  1367. if let dataMessage = data["message"] as? TMessage {
  1368. let chatData = dataMessage.mBodies
  1369. if (dataMessage.getCode() == CoreMessage_TMessageCode.PUSH_MEMBER_ROOM_CONTACT_CENTER && isContactCenter) {
  1370. let data = dataMessage.getBody(key: CoreMessage_TMessageKey.DATA)
  1371. if !data.isEmpty {
  1372. if let jsonArray = try! JSONSerialization.jsonObject(with: data.data(using: String.Encoding.utf8)!, options: JSONSerialization.ReadingOptions()) as? [AnyObject] {
  1373. var members = ""
  1374. let idMe = User.getMyPin()!
  1375. var user : [User] = []
  1376. for json in jsonArray {
  1377. if "\(json)" != idMe {
  1378. if members.isEmpty {
  1379. members = "\(json)"
  1380. } else {
  1381. members += ",\(json)"
  1382. }
  1383. if let userData = User.getData(pin: "\(json)") {
  1384. user.append(userData)
  1385. } else {
  1386. Nexilis.addFriend (fpin: "\(json)") { result in
  1387. DispatchQueue.main.async {
  1388. if result {
  1389. let userData = User.getData(pin: "\(json)")!
  1390. user.append(userData)
  1391. } else {
  1392. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  1393. imageView.tintColor = .white
  1394. let banner = FloatingNotificationBanner(title: "Server busy, please try again later".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
  1395. banner.show()
  1396. }
  1397. }
  1398. }
  1399. }
  1400. }
  1401. }
  1402. self.users = user
  1403. self.fPinContacCenter = members
  1404. self.changeAppBar()
  1405. SecureUserDefaults.shared.set(members, forKey: "inEditorPersonal")
  1406. }
  1407. }
  1408. } else if (dataMessage.getCode() == CoreMessage_TMessageCode.ACCEPT_CALL_CENTER) {
  1409. if !self.isRequestContactCenter || !isContactCenter {
  1410. return
  1411. }
  1412. SecureUserDefaults.shared.set(dataMessage.getBody(key: CoreMessage_TMessageKey.F_PIN), forKey: "inEditorPersonal")
  1413. let date = Date()
  1414. let formatter = DateFormatter()
  1415. formatter.dateFormat = "HH:mm"
  1416. formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
  1417. self.fPinContacCenter = dataMessage.getBody(key: CoreMessage_TMessageKey.F_PIN)
  1418. self.complaintId = dataMessage.getBody(key: CoreMessage_TMessageKey.DATA)
  1419. self.channelContactCenter = dataMessage.getBody(key: CoreMessage_TMessageKey.CHANNEL)
  1420. var row: [String: Any?] = [:]
  1421. row["category_cc"] = "You are connecting with ".localized() + dataMessage.getBody(key: CoreMessage_TMessageKey.F_DISPLAY_NAME).trimmingCharacters(in: .whitespaces) + " at ".localized() + formatter.string(from: date as Date) + ".\n" + "In order to improve our service, all conversations will be recorded\naccording to state regulations".localized()
  1422. row["message_id"] = ""
  1423. row["message_text"] = "You are connecting with ".localized() + dataMessage.getBody(key: CoreMessage_TMessageKey.F_DISPLAY_NAME).trimmingCharacters(in: .whitespaces) + " at ".localized() + formatter.string(from: date as Date) + ".\n" + "In order to improve our service, all conversations will be recorded\naccording to state regulations".localized()
  1424. row["chat_date"] = "Today".localized()
  1425. self.dataMessages.append(row)
  1426. self.users.append(User.getData(pin: dataMessage.getBody(key: CoreMessage_TMessageKey.F_PIN))!)
  1427. self.changeAppBar()
  1428. self.setRightButtonItem()
  1429. self.dateStartCC = "\(Date().currentTimeMillis())"
  1430. self.tableChatView.insertRows(at: [IndexPath(row: self.dataMessages.count - 1, section: 0)], with: .none)
  1431. self.tableChatView.scrollToBottom()
  1432. SecureUserDefaults.shared.removeValue(forKey: "waitingRequestCC")
  1433. if dataMessage.getBody(key: CoreMessage_TMessageKey.CHANNEL) != "0" {
  1434. SecureUserDefaults.shared.set("\(Date().currentTimeMillis())", forKey: "startTimeCC")
  1435. SecureUserDefaults.shared.set(dataMessage.getBody(key: CoreMessage_TMessageKey.CHANNEL), forKey: "channelCC")
  1436. DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
  1437. self.dismiss(animated: true, completion: {
  1438. self.removeAllObjectBeforeDismissVC()
  1439. })
  1440. })
  1441. } else {
  1442. viewButton.isHidden = false
  1443. viewTextfield.isHidden = false
  1444. }
  1445. } else if (dataMessage.getCode() == CoreMessage_TMessageCode.INVITE_END_CONTACT_CENTER || dataMessage.getCode() == CoreMessage_TMessageCode.END_CALL_CENTER || dataMessage.getCode() == CoreMessage_TMessageCode.INVITE_EXIT_CONTACT_CENTER) && !fromVCAC {
  1446. let onGoingCC: String = SecureUserDefaults.shared.value(forKey: "onGoingCC") ?? ""
  1447. if onGoingCC.isEmpty || !isContactCenter {
  1448. return
  1449. }
  1450. let requester = onGoingCC.components(separatedBy: ",")[0]
  1451. let officer = onGoingCC.isEmpty ? "" : onGoingCC.components(separatedBy: ",")[1]
  1452. let fPin = dataMessage.getCode() == CoreMessage_TMessageCode.END_CALL_CENTER ? chatData[CoreMessage_TMessageKey.F_PIN] : dataMessage.getPIN()
  1453. if fPin == officer || fPin == requester {
  1454. DispatchQueue.global().async {
  1455. let date = "\(Date().currentTimeMillis())"
  1456. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  1457. do {
  1458. _ = try Database.shared.insertRecord(fmdb: fmdb, table: "CALL_CENTER_HISTORY", cvalues: [
  1459. "type" : self.channelContactCenter,
  1460. "title" : "Contact Center".localized(),
  1461. "time" : self.dateStartCC,
  1462. "f_pin" : officer,
  1463. "data" : self.complaintId,
  1464. "time_end" : date,
  1465. "complaint_id" : self.complaintId,
  1466. "members" : "",
  1467. "requester": requester
  1468. ], replace: true)
  1469. } catch {
  1470. rollback.pointee = true
  1471. print("Access database error: \(error.localizedDescription)")
  1472. }
  1473. })
  1474. }
  1475. self.dismissKeyboard()
  1476. self.disableEditor()
  1477. SecureUserDefaults.shared.removeValue(forKey: "onGoingCC")
  1478. SecureUserDefaults.shared.removeValue(forKey: "membersCC")
  1479. SecureUserDefaults.shared.removeValue(forKey: "waitingRequestCC")
  1480. DispatchQueue.main.async {
  1481. let imageView = UIImageView(image: UIImage(systemName: "info.circle"))
  1482. imageView.tintColor = .white
  1483. let banner = FloatingNotificationBanner(title: "Call Center Session has ended".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .info, colors: nil, iconPosition: .center)
  1484. banner.show()
  1485. }
  1486. timeoutCC.invalidate()
  1487. DispatchQueue.main.asyncAfter(deadline: .now() + 1.5, execute: {
  1488. if !(self.presentedViewController is EditorPersonal) {
  1489. self.dismiss(animated: true, completion: {
  1490. self.removeAllObjectBeforeDismissVC()
  1491. })
  1492. }
  1493. DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
  1494. self.dismiss(animated: true, completion: {
  1495. self.removeAllObjectBeforeDismissVC()
  1496. })
  1497. }
  1498. })
  1499. } else {
  1500. var members = ""
  1501. self.users.removeAll(where: {$0.pin == fPin})
  1502. for user in self.users {
  1503. if members.isEmpty {
  1504. members = "\(user.pin)"
  1505. } else {
  1506. members = ",\(user.pin)"
  1507. }
  1508. }
  1509. SecureUserDefaults.shared.set("\(members)", forKey: "membersCC")
  1510. self.fPinContacCenter = members
  1511. self.changeAppBar()
  1512. }
  1513. }
  1514. else if (chatData[CoreMessage_TMessageKey.F_PIN] == self.dataPerson["f_pin"]!! && !self.isContactCenter && (chatData[CoreMessage_TMessageKey.MESSAGE_SCOPE_ID] == "3")) || (self.isContactCenter && chatData[CoreMessage_TMessageKey.MESSAGE_SCOPE_ID] == "5" && self.complaintId == chatData[CoreMessage_TMessageKey.CALL_CENTER_ID]) {
  1515. if chatData[CoreMessage_TMessageKey.F_PIN] == nil {
  1516. return
  1517. }
  1518. let idx = self.dataMessages.firstIndex(where: { $0[TypeDataMessage.message_id] as? String == chatData[CoreMessage_TMessageKey.MESSAGE_ID]})
  1519. if idx != nil {
  1520. self.dataMessages[idx!][TypeDataMessage.message_text] = chatData[CoreMessage_TMessageKey.MESSAGE_TEXT]
  1521. self.dataMessages[idx!][TypeDataMessage.last_edit] = Int64(chatData[CoreMessage_TMessageKey.LAST_EDIT]!)
  1522. self.dataMessages[idx!][TypeDataMessage.status] = chatData[CoreMessage_TMessageKey.STATUS]
  1523. let section = self.dataDates.firstIndex(of: self.dataMessages[idx!]["chat_date"] as? String ?? "")
  1524. let row = self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == self.dataMessages[idx!]["chat_date"] as? String ?? ""}).firstIndex(where: { $0["message_id"] as? String == self.dataMessages[idx!]["message_id"] as? String })
  1525. if row != nil && section != nil {
  1526. self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
  1527. }
  1528. return
  1529. }
  1530. var row: [String: Any?] = [:]
  1531. row["message_id"] = chatData[CoreMessage_TMessageKey.MESSAGE_ID]
  1532. row["f_pin"] = chatData[CoreMessage_TMessageKey.F_PIN]
  1533. row["l_pin"] = chatData[CoreMessage_TMessageKey.L_PIN]
  1534. row["message_scope_id"] = chatData[CoreMessage_TMessageKey.MESSAGE_SCOPE_ID]
  1535. row["server_date"] = chatData[CoreMessage_TMessageKey.SERVER_DATE]
  1536. row["status"] = chatData[CoreMessage_TMessageKey.STATUS]
  1537. row["message_text"] = chatData[CoreMessage_TMessageKey.MESSAGE_TEXT]
  1538. if (chatData.keys.contains(CoreMessage_TMessageKey.AUDIO_ID)) {
  1539. row["audio_id"] = chatData[CoreMessage_TMessageKey.AUDIO_ID]
  1540. } else {
  1541. row["audio_id"] = ""
  1542. }
  1543. if (chatData.keys.contains(CoreMessage_TMessageKey.GIF_ID)) {
  1544. row["gif_id"] = chatData[CoreMessage_TMessageKey.GIF_ID]
  1545. } else {
  1546. row["gif_id"] = ""
  1547. }
  1548. if (chatData.keys.contains(CoreMessage_TMessageKey.VIDEO_ID)) {
  1549. row["video_id"] = chatData[CoreMessage_TMessageKey.VIDEO_ID]
  1550. } else {
  1551. row["video_id"] = ""
  1552. }
  1553. if (chatData.keys.contains(CoreMessage_TMessageKey.IMAGE_ID)) {
  1554. row["image_id"] = chatData[CoreMessage_TMessageKey.IMAGE_ID]
  1555. } else {
  1556. row["image_id"] = ""
  1557. }
  1558. if (chatData.keys.contains(CoreMessage_TMessageKey.THUMB_ID)) {
  1559. row["thumb_id"] = chatData[CoreMessage_TMessageKey.THUMB_ID]
  1560. } else {
  1561. row["thumb_id"] = ""
  1562. }
  1563. if (chatData.keys.contains(CoreMessage_TMessageKey.READ_RECEIPTS)) {
  1564. row["read_receipts"] = chatData[CoreMessage_TMessageKey.READ_RECEIPTS]
  1565. } else {
  1566. row["read_receipts"] = ""
  1567. }
  1568. if (chatData.keys.contains(CoreMessage_TMessageKey.CREDENTIAL)) {
  1569. row["credential"] = chatData[CoreMessage_TMessageKey.CREDENTIAL]
  1570. } else {
  1571. row["credential"] = ""
  1572. }
  1573. row["chat_id"] = ""
  1574. if (chatData.keys.contains(CoreMessage_TMessageKey.FILE_ID)) {
  1575. row["file_id"] = chatData[CoreMessage_TMessageKey.FILE_ID]
  1576. } else {
  1577. row["file_id"] = ""
  1578. }
  1579. row["progress"] = 0.0
  1580. row["attachment_flag"] = chatData[CoreMessage_TMessageKey.ATTACHMENT_FLAG]
  1581. row["reff_id"] = chatData[CoreMessage_TMessageKey.REF_ID] ?? ""
  1582. row["lock"] = ""
  1583. row["is_stared"] = "0"
  1584. row[TypeDataMessage.is_forwarded] = Int(chatData[CoreMessage_TMessageKey.IS_FORWARDED_MESSAGE] ?? "0")
  1585. row["isSelected"] = false
  1586. if !self.dataDates.contains("Today".localized()) {
  1587. self.dataDates.append("Today".localized())
  1588. self.tableChatView.insertSections(IndexSet(integer: self.dataDates.count - 1), with: .none)
  1589. }
  1590. row["chat_date"] = "Today".localized()
  1591. row["blog_id"] = chatData[CoreMessage_TMessageKey.BLOG_ID]
  1592. self.counter += 1
  1593. if row["credential"] != nil && row["credential"] as? String ?? "" == "1" {
  1594. self.listTimerCredential[row["message_id"] as? String ?? ""] = 60
  1595. }
  1596. self.dataMessages.append(row)
  1597. self.tableChatView.insertRows(at: [IndexPath(row: self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == self.dataDates[self.dataDates.count - 1]}).count - 1, section: self.dataDates.count - 1)], with: .none)
  1598. if row["credential"] != nil && row["credential"] as? String ?? "" == "1" {
  1599. var timer = Timer()
  1600. var minute = 60
  1601. self.timerCredential[row["message_id"] as? String ?? ""] = timer
  1602. SecureUserDefaults.shared.set("\(Date().currentTimeMillis())", forKey: row["message_id"] as? String ?? "")
  1603. timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: {_ in
  1604. minute -= 1
  1605. self.listTimerCredential[row["message_id"] as? String ?? ""] = minute
  1606. if minute == 0 {
  1607. timer.invalidate()
  1608. self.listTimerCredential.removeValue(forKey: row["message_id"] as? String ?? "")
  1609. self.timerCredential.removeValue(forKey: row["message_id"] as? String ?? "")
  1610. SecureUserDefaults.shared.removeValue(forKey: row["message_id"] as? String ?? "")
  1611. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == row["message_id"] as? String})
  1612. if idx != nil {
  1613. self.dataMessages[idx!]["lock"] = "2"
  1614. self.dataMessages[idx!]["reff_id"] = ""
  1615. }
  1616. DispatchQueue.global().async {
  1617. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  1618. do {
  1619. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  1620. "lock" : "2"
  1621. ], _where: "message_id = '\(row["message_id"] as? String ?? "")'")
  1622. } catch {
  1623. rollback.pointee = true
  1624. print("Access database error: \(error.localizedDescription)")
  1625. }
  1626. })
  1627. }
  1628. }
  1629. let section = self.dataDates.firstIndex(of: self.dataDates[self.dataDates.count - 1])
  1630. let row = self.dataMessages.filter({$0["chat_date"] as? String ?? "" == self.dataDates[self.dataDates.count - 1]}).firstIndex(where: { $0["message_id"] as? String == row["message_id"] as? String})
  1631. if row != nil && section != nil{
  1632. self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
  1633. }
  1634. })
  1635. }
  1636. if self.isContactCenter {
  1637. let idMe = User.getMyPin()!
  1638. let onGoingCC: String = SecureUserDefaults.shared.value(forKey: "onGoingCC") ?? ""
  1639. let officer = onGoingCC.isEmpty ? "" : onGoingCC.components(separatedBy: ",")[1]
  1640. if officer == idMe {
  1641. self.timeoutCC.invalidate()
  1642. } else {
  1643. if !self.showToast30s {
  1644. self.view.makeToast("Please reply within 30 seconds so the call center session doesn't end.".localized(), duration: 3)
  1645. sendTyping(l_pin: fPinContacCenter, isTyping: true)
  1646. self.showToast30s = true
  1647. }
  1648. }
  1649. }
  1650. if chatData[CoreMessage_TMessageKey.FORMAT] == "1" {
  1651. self.sendReadMessageStatus(chat_id: "", f_pin: chatData[CoreMessage_TMessageKey.F_PIN]!, message_scope_id: chatData[CoreMessage_TMessageKey.MESSAGE_SCOPE_ID]!, message_id: chatData[CoreMessage_TMessageKey.MESSAGE_ID]!)
  1652. self.tableChatView.scrollToBottom()
  1653. } else if self.currentIndexpath?.row == (self.dataMessages.count - 2) {
  1654. if (self.viewIfLoaded?.window != nil) {
  1655. self.sendReadMessageStatus(chat_id: "", f_pin: chatData[CoreMessage_TMessageKey.F_PIN]!, message_scope_id: chatData[CoreMessage_TMessageKey.MESSAGE_SCOPE_ID]!, message_id: chatData[CoreMessage_TMessageKey.MESSAGE_ID]!)
  1656. }
  1657. self.tableChatView.scrollToBottom()
  1658. if ( self.currentIndexpath!.section <= self.dataDates.count - 1 && self.currentIndexpath!.row <= self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == self.dataDates[self.dataDates.count - 1]}).count - 1) {
  1659. self.counter = 0
  1660. self.updateCounter(counter: self.counter)
  1661. }
  1662. let lastMarkerCounter = markerCounter
  1663. if self.markerCounter != nil {
  1664. self.markerCounter = nil
  1665. }
  1666. self.tableChatView.beginUpdates()
  1667. let indexMessage = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == lastMarkerCounter })
  1668. if indexMessage != nil {
  1669. let section = self.dataDates.firstIndex(of: self.dataMessages[indexMessage!]["chat_date"] as? String ?? "")
  1670. let row = self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == self.dataMessages[indexMessage!]["chat_date"] as? String ?? ""}).firstIndex(where: { $0["message_id"] as? String == self.dataMessages[indexMessage!]["message_id"] as? String })
  1671. if row != nil && section != nil {
  1672. self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
  1673. }
  1674. }
  1675. self.tableChatView.endUpdates()
  1676. }
  1677. else if self.currentIndexpath == nil {
  1678. self.counter = 0
  1679. self.updateCounter(counter: self.counter)
  1680. if (self.viewIfLoaded?.window != nil) {
  1681. self.sendReadMessageStatus(chat_id: "", f_pin: chatData[CoreMessage_TMessageKey.F_PIN]!, message_scope_id: chatData[CoreMessage_TMessageKey.MESSAGE_SCOPE_ID]!, message_id: chatData[CoreMessage_TMessageKey.MESSAGE_ID]!)
  1682. }
  1683. }
  1684. else if self.counter != 0 {
  1685. if !self.indicatorCounterBSTB.isDescendant(of: self.view) && self.buttonScrollToBottom.isDescendant(of: self.view) {
  1686. self.markerCounter = row["message_id"] as? String
  1687. self.addCounterAtButttonScrollToBottom()
  1688. self.tableChatView.beginUpdates()
  1689. let indexMessage = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == self.markerCounter })
  1690. if indexMessage != nil {
  1691. let section = self.dataDates.firstIndex(of: self.dataMessages[indexMessage!]["chat_date"] as? String ?? "")
  1692. let row = self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == self.dataMessages[indexMessage!]["chat_date"] as? String ?? ""}).firstIndex(where: { $0["message_id"] as? String == self.dataMessages[indexMessage!]["message_id"] as? String })
  1693. if row != nil && section != nil {
  1694. self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
  1695. }
  1696. }
  1697. self.tableChatView.endUpdates()
  1698. } else if self.indicatorCounterBSTB.isDescendant(of: self.view) {
  1699. self.labelCounter.text = "\(self.counter)"
  1700. }
  1701. }
  1702. }
  1703. } else if !self.isContactCenter {
  1704. NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
  1705. }
  1706. }
  1707. }
  1708. private func disableEditor() {
  1709. view.addSubview(containerAction)
  1710. containerAction.translatesAutoresizingMaskIntoConstraints = false
  1711. NSLayoutConstraint.activate([
  1712. containerAction.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
  1713. containerAction.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
  1714. containerAction.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
  1715. containerAction.heightAnchor.constraint(equalToConstant: 120)
  1716. ])
  1717. containerAction.backgroundColor = .secondaryColor.withAlphaComponent(0.8)
  1718. let labelDisable = UILabel()
  1719. containerAction.addSubview(labelDisable)
  1720. labelDisable.translatesAutoresizingMaskIntoConstraints = false
  1721. NSLayoutConstraint.activate([
  1722. labelDisable.centerYAnchor.constraint(equalTo: containerAction.centerYAnchor),
  1723. labelDisable.centerXAnchor.constraint(equalTo: containerAction.centerXAnchor),
  1724. ])
  1725. labelDisable.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
  1726. labelDisable.font = UIFont.systemFont(ofSize: 12).bold
  1727. labelDisable.text = "Call center session is over".localized()
  1728. }
  1729. @objc func onStatusChat(notification: NSNotification) {
  1730. DispatchQueue.main.async {
  1731. let data:[AnyHashable : Any] = notification.userInfo!
  1732. if let dataMessage = data["message"] as? TMessage {
  1733. let chatData = dataMessage.mBodies
  1734. let onGoingCC: String = SecureUserDefaults.shared.value(forKey: "onGoingCC") ?? ""
  1735. let requester = onGoingCC.components(separatedBy: ",")[0]
  1736. let idMe = User.getMyPin()!
  1737. if chatData[CoreMessage_TMessageKey.F_PIN] == self.dataPerson["f_pin"]!! || chatData[CoreMessage_TMessageKey.L_PIN] == self.dataPerson["f_pin"]!! || chatData[CoreMessage_TMessageKey.L_PIN] == self.fPinContacCenter || requester == idMe {
  1738. if (chatData.keys.contains(CoreMessage_TMessageKey.MESSAGE_ID) && !(chatData[CoreMessage_TMessageKey.MESSAGE_ID]!).contains("-2,")) {
  1739. var idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == chatData[CoreMessage_TMessageKey.MESSAGE_ID]! })
  1740. if let idxMessageIdParent = self.groupImages.firstIndex(where: { $0.value.contains(where: { $0.messageId == chatData[CoreMessage_TMessageKey.MESSAGE_ID]! }) }) {
  1741. if let idxInImages = self.groupImages[idxMessageIdParent].value.firstIndex(where: { $0.messageId == chatData[CoreMessage_TMessageKey.MESSAGE_ID]! }) {
  1742. self.groupImages[idxMessageIdParent].value[idxInImages].status = chatData[CoreMessage_TMessageKey.STATUS]!
  1743. self.groupImages[idxMessageIdParent].value[idxInImages].dataMessage["status"] = chatData[CoreMessage_TMessageKey.STATUS]!
  1744. }
  1745. idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == self.groupImages[idxMessageIdParent].key })
  1746. }
  1747. if (idx != nil) {
  1748. if (chatData[CoreMessage_TMessageKey.DELETE_MESSAGE_FLAG] == "1") {
  1749. self.updateStatusDelete(idx: idx, chatData: chatData)
  1750. } else {
  1751. self.updateStatusMessage(idx: idx, chatData: chatData)
  1752. }
  1753. }
  1754. }
  1755. else if (chatData.keys.contains("message_id")) {
  1756. var idMessage = dataMessage.getBody(key: "message_id")
  1757. if idMessage.contains("'") {
  1758. idMessage = idMessage.replacingOccurrences(of: "'", with: "")
  1759. }
  1760. var idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == idMessage })
  1761. if let idxMessageIdParent = self.groupImages.firstIndex(where: { $0.value.contains(where: { $0.messageId == idMessage }) }) {
  1762. if let idxInImages = self.groupImages[idxMessageIdParent].value.firstIndex(where: { $0.messageId == idMessage }) {
  1763. self.groupImages[idxMessageIdParent].value[idxInImages].status = chatData[CoreMessage_TMessageKey.STATUS]!
  1764. self.groupImages[idxMessageIdParent].value[idxInImages].dataMessage["status"] = chatData[CoreMessage_TMessageKey.STATUS]!
  1765. }
  1766. idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == self.groupImages[idxMessageIdParent].key })
  1767. }
  1768. if (idx != nil) {
  1769. if (chatData[CoreMessage_TMessageKey.DELETE_MESSAGE_FLAG] == "1") {
  1770. self.updateStatusDelete(idx: idx, chatData: chatData)
  1771. } else {
  1772. self.updateStatusMessage(idx: idx, chatData: chatData)
  1773. }
  1774. }
  1775. }
  1776. else {
  1777. let listMessageId = chatData[CoreMessage_TMessageKey.MESSAGE_ID]!.split(separator: ",")
  1778. for i in 1..<listMessageId.count {
  1779. var idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String ?? "" == listMessageId[i] })
  1780. if let idxMessageIdParent = self.groupImages.firstIndex(where: { $0.value.contains(where: { $0.messageId == listMessageId[i] }) }) {
  1781. if let idxInImages = self.groupImages[idxMessageIdParent].value.firstIndex(where: { $0.messageId == listMessageId[i] }) {
  1782. self.groupImages[idxMessageIdParent].value[idxInImages].status = chatData[CoreMessage_TMessageKey.STATUS]!
  1783. self.groupImages[idxMessageIdParent].value[idxInImages].dataMessage["status"] = chatData[CoreMessage_TMessageKey.STATUS]!
  1784. }
  1785. idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == self.groupImages[idxMessageIdParent].key })
  1786. }
  1787. if (idx != nil) {
  1788. self.updateStatusMessage(idx: idx, chatData: chatData)
  1789. }
  1790. }
  1791. }
  1792. }
  1793. }
  1794. }
  1795. }
  1796. @objc func onFailedSendMessage(notification: NSNotification) {
  1797. DispatchQueue.main.async {
  1798. let data:[AnyHashable : Any] = notification.userInfo!
  1799. let messageId = data["message_id"] as? String ?? ""
  1800. let status = data["status"] as? String ?? ""
  1801. var idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String ?? "" == messageId })
  1802. if let idxMessageIdParent = self.groupImages.firstIndex(where: { $0.value.contains(where: { $0.messageId == messageId }) }) {
  1803. if let idxInImages = self.groupImages[idxMessageIdParent].value.firstIndex(where: { $0.messageId == messageId }) {
  1804. self.groupImages[idxMessageIdParent].value[idxInImages].status = status
  1805. self.groupImages[idxMessageIdParent].value[idxInImages].dataMessage["status"] = status
  1806. }
  1807. idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == self.groupImages[idxMessageIdParent].key })
  1808. }
  1809. if (idx != nil) {
  1810. do {
  1811. self.dataMessages[idx!]["status"] = status
  1812. let section = self.dataDates.firstIndex(of: self.dataMessages[idx!]["chat_date"] as? String ?? "")
  1813. let row = self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == self.dataMessages[idx!]["chat_date"] as? String ?? ""}).firstIndex(where: { $0["message_id"] as? String == self.dataMessages[idx!]["message_id"] as? String })
  1814. if row != nil && section != nil {
  1815. self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
  1816. }
  1817. } catch {
  1818. }
  1819. }
  1820. }
  1821. }
  1822. private func updateStatusDelete(idx: Int?, chatData: [String: String]) {
  1823. do {
  1824. if self.dataMessages[idx!]["lock"] != nil && self.dataMessages[idx!]["lock"] as? String ?? "" == "1" {
  1825. return
  1826. }
  1827. self.dataMessages[idx!]["lock"] = "1"
  1828. self.dataMessages[idx!]["reff_id"] = ""
  1829. let section = self.dataDates.firstIndex(of: self.dataMessages[idx!]["chat_date"] as? String ?? "")
  1830. let row = self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == self.dataMessages[idx!]["chat_date"] as? String ?? ""}).firstIndex(where: { $0["message_id"] as? String == self.dataMessages[idx!]["message_id"] as? String })
  1831. if row != nil && section != nil {
  1832. self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
  1833. }
  1834. if self.listTimerCredential[self.dataMessages[idx!]["message_id"] as? String ?? ""] != nil {
  1835. self.listTimerCredential.removeValue(forKey: self.dataMessages[idx!]["message_id"] as? String ?? "")
  1836. self.timerCredential[self.dataMessages[idx!]["message_id"] as? String ?? ""]?.invalidate()
  1837. self.timerCredential.removeValue(forKey: self.dataMessages[idx!]["message_id"] as? String ?? "")
  1838. SecureUserDefaults.shared.removeValue(forKey: self.dataMessages[idx!]["message_id"] as? String ?? "")
  1839. }
  1840. if self.reffId != nil && self.reffId == chatData["message_id"]! {
  1841. self.deleteReplyView()
  1842. }
  1843. } catch {
  1844. }
  1845. }
  1846. private func updateStatusMessage(idx: Int?, chatData: [String: String]) {
  1847. do {
  1848. if Int(self.dataMessages[idx!]["status"] as? String ?? "")! > Int(chatData[CoreMessage_TMessageKey.STATUS]!)! {
  1849. return
  1850. }
  1851. self.dataMessages[idx!]["status"] = chatData[CoreMessage_TMessageKey.STATUS]!
  1852. let section = self.dataDates.firstIndex(of: self.dataMessages[idx!]["chat_date"] as? String ?? "")
  1853. let row = self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == self.dataMessages[idx!]["chat_date"] as? String ?? ""}).firstIndex(where: { $0["message_id"] as? String == self.dataMessages[idx!]["message_id"] as? String })
  1854. if row != nil && section != nil {
  1855. self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
  1856. }
  1857. } catch {
  1858. }
  1859. }
  1860. @objc func onTyping(notification: NSNotification) {
  1861. DispatchQueue.main.async { [self] in
  1862. let data:[AnyHashable : Any] = notification.userInfo!
  1863. let message: TMessage = data["message"] as! TMessage
  1864. let onGoingCC: String = SecureUserDefaults.shared.value(forKey: "onGoingCC") ?? ""
  1865. if !onGoingCC.isEmpty {
  1866. let officer = onGoingCC.isEmpty ? "" : onGoingCC.components(separatedBy: ",")[1]
  1867. if message.getBody(key: CoreMessage_TMessageKey.F_PIN) != officer {
  1868. //print("RESET TIMER")
  1869. // timeoutCC.invalidate()
  1870. // timeoutCC = Timer.scheduledTimer(withTimeInterval: 30.0, repeats: false, block: {_ in
  1871. // let imageView = UIImageView(image: UIImage(systemName: "info.circle"))
  1872. // imageView.tintColor = .white
  1873. // let banner = FloatingNotificationBanner(title: "Customer doesn't respond in 30 second, so call center session will be ended automatically.".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .info, colors: nil, iconPosition: .center)
  1874. // banner.show()
  1875. // self.endCallCenter()
  1876. // })
  1877. }
  1878. } else {
  1879. }
  1880. }
  1881. }
  1882. @objc func onUnfriend(notification: NSNotification) {
  1883. let data:[AnyHashable : Any] = notification.userInfo!
  1884. DispatchQueue.main.async { [self] in
  1885. if data["state"] as! Int == 99 && (data["message"] as? String ?? "").components(separatedBy: ",")[0] == "delete_buddy" {
  1886. removed = true
  1887. if forwardSession || copySession || deleteSession || isSearching {
  1888. cancelAction()
  1889. }
  1890. DispatchQueue.main.asyncAfter(deadline: .now() + 0.35, execute: {[self] in
  1891. navigationItem.rightBarButtonItem = nil
  1892. navigationItem.rightBarButtonItems = nil
  1893. changeAppBar()
  1894. view.addSubview(containerAction)
  1895. containerAction.translatesAutoresizingMaskIntoConstraints = false
  1896. NSLayoutConstraint.activate([
  1897. containerAction.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
  1898. containerAction.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
  1899. containerAction.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
  1900. containerAction.heightAnchor.constraint(equalToConstant: 120)
  1901. ])
  1902. containerAction.backgroundColor = .secondaryColor.withAlphaComponent(0.8)
  1903. let labelUnfriend = UILabel()
  1904. containerAction.addSubview(labelUnfriend)
  1905. labelUnfriend.translatesAutoresizingMaskIntoConstraints = false
  1906. NSLayoutConstraint.activate([
  1907. labelUnfriend.centerYAnchor.constraint(equalTo: containerAction.centerYAnchor),
  1908. labelUnfriend.centerXAnchor.constraint(equalTo: containerAction.centerXAnchor),
  1909. ])
  1910. labelUnfriend.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
  1911. labelUnfriend.font = UIFont.systemFont(ofSize: 12).bold
  1912. labelUnfriend.text = "You have unfriended this user".localized()
  1913. NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
  1914. if contactChatNav.viewIfLoaded?.window != nil {
  1915. contactChatNav.dismiss(animated: true)
  1916. }
  1917. DispatchQueue.main.asyncAfter(deadline: .now() + 0.35, execute: {
  1918. if self.fromNotification {
  1919. self.didTapExit()
  1920. } else {
  1921. self.navigationController?.popViewController(animated: true)
  1922. }
  1923. })
  1924. })
  1925. } else if data["state"] as! Int == 01 {
  1926. if let dataMessage = try! JSONSerialization.jsonObject(with: (data["message"] as? String ?? "").data(using: .utf8)!, options: []) as? [String: String] {
  1927. if(dataMessage["l_pin"] == dataPerson["f_pin"]!){
  1928. if let block = dataMessage["block"] {
  1929. if(block == "-1"){
  1930. dismissKeyboard()
  1931. }
  1932. blockedView(blocked: block)
  1933. if contactChatNav.viewIfLoaded?.window != nil {
  1934. contactChatNav.dismiss(animated: true)
  1935. }
  1936. cancelAction()
  1937. }
  1938. }
  1939. setRightButtonItem()
  1940. }
  1941. }
  1942. }
  1943. }
  1944. func blockedView(blocked: String) {
  1945. dismissKeyboard()
  1946. view.addSubview(containerAction)
  1947. containerAction.translatesAutoresizingMaskIntoConstraints = false
  1948. NSLayoutConstraint.activate([
  1949. containerAction.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
  1950. containerAction.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
  1951. containerAction.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
  1952. containerAction.heightAnchor.constraint(equalToConstant: 120)
  1953. ])
  1954. containerAction.backgroundColor = .secondaryColor.withAlphaComponent(0.8)
  1955. let labelBlocked = UILabel()
  1956. containerAction.addSubview(labelBlocked)
  1957. labelBlocked.translatesAutoresizingMaskIntoConstraints = false
  1958. NSLayoutConstraint.activate([
  1959. labelBlocked.centerYAnchor.constraint(equalTo: containerAction.centerYAnchor),
  1960. labelBlocked.centerXAnchor.constraint(equalTo: containerAction.centerXAnchor),
  1961. ])
  1962. labelBlocked.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
  1963. labelBlocked.font = UIFont.systemFont(ofSize: 12).bold
  1964. if blocked == "1" {
  1965. labelBlocked.text = "You blocked this user".localized()
  1966. } else {
  1967. labelBlocked.text = "You have been blocked by this user".localized()
  1968. }
  1969. }
  1970. @objc func seeProfileTapped() {
  1971. if dataPerson["f_pin"] == "-999" || dataPerson["isOfficial"] == "1" || removed || copySession || forwardSession || deleteSession || isContactCenter {
  1972. return
  1973. }
  1974. dismissKeyboard()
  1975. let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "profileView") as! ProfileViewController
  1976. controller.data = dataPerson["f_pin"]!!
  1977. controller.checkReadMessage = {
  1978. self.dataPerson.removeAll()
  1979. self.getDataProfile(fPin: self.unique_l_pin)
  1980. self.changeAppBar()
  1981. if self.currentIndexpath == nil {
  1982. var listData = self.dataMessages
  1983. listData = listData.filter({$0["status"] as? String ?? "" != "4" && $0["status"] as? String ?? "" != "8"})
  1984. if listData.count != 0 && !self.isContactCenter {
  1985. let idMe = User.getMyPin() as String?
  1986. for i in 0...listData.count - 1 {
  1987. if listData[i]["f_pin"] as? String != idMe {
  1988. self.sendReadMessageStatus(chat_id: "", f_pin: self.dataPerson["f_pin"]!!, message_scope_id: "3", message_id: listData[i]["message_id"] as? String ?? "")
  1989. }
  1990. }
  1991. }
  1992. } else {
  1993. let dataMessages = self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == self.dataDates[self.currentIndexpath!.section] })
  1994. var listData = dataMessages
  1995. listData = listData.filter({$0["status"] as? String ?? "" != "4" && $0["status"] as? String ?? "" != "8"})
  1996. if listData.count != 0 && !self.isContactCenter {
  1997. let idMe = User.getMyPin() as String?
  1998. for i in 0...listData.count - 1 {
  1999. if listData[i]["f_pin"] as? String != idMe {
  2000. self.sendReadMessageStatus(chat_id: "", f_pin: self.dataPerson["f_pin"]!!, message_scope_id: "3", message_id: listData[i]["message_id"] as? String ?? "")
  2001. }
  2002. }
  2003. }
  2004. }
  2005. }
  2006. navigationController?.show(controller, sender: nil)
  2007. }
  2008. @IBAction func voiceTapped(_ sender: UIButton) {
  2009. if (self.constraintBottomAttachment.constant != 0.0) {
  2010. constraintBottomAttachment.constant = 0.0
  2011. self.viewSticker.removeConstraints(self.viewSticker.constraints)
  2012. self.viewSticker.removeFromSuperview()
  2013. }
  2014. }
  2015. @IBAction func imageTapped(_ sender: UIButton) {
  2016. if (self.constraintBottomAttachment.constant != 0.0) {
  2017. constraintBottomAttachment.constant = 0.0
  2018. self.viewSticker.removeConstraints(self.viewSticker.constraints)
  2019. self.viewSticker.removeFromSuperview()
  2020. }
  2021. if isContactCenter && fPinContacCenter.isEmpty && isRequestContactCenter {
  2022. return
  2023. }
  2024. let alertController = LibAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
  2025. if let action = self.actionImageVideo(for: "image", title: "Choose Photo".localized()) {
  2026. alertController.addAction(action)
  2027. }
  2028. if let action = self.actionImageVideo(for: "video", title: "Choose Video".localized()) {
  2029. alertController.addAction(action)
  2030. }
  2031. alertController.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: nil))
  2032. self.present(alertController, animated: true)
  2033. }
  2034. private func actionImageVideo(for type: String, title: String) -> UIAlertAction? {
  2035. return UIAlertAction(title: title, style: .default) { [unowned self] _ in
  2036. switch type {
  2037. case "image":
  2038. var config = PHPickerConfiguration()
  2039. config.filter = .images
  2040. let picker = PHPickerViewController(configuration: config)
  2041. picker.delegate = self
  2042. if UIBarButtonItem.appearance().titleTextAttributes(for: .normal) != nil {
  2043. isBlackCancelButton = UIBarButtonItem.appearance().titleTextAttributes(for: .normal)?.values.first as! NSObject == UIColor.black
  2044. }
  2045. if !isBlackCancelButton {
  2046. let cancelButtonAttributes = [NSAttributedString.Key.foregroundColor: UIColor.black, NSAttributedString.Key.font : UIFont.systemFont(ofSize: 16)]
  2047. UIBarButtonItem.appearance().setTitleTextAttributes(cancelButtonAttributes , for: .normal)
  2048. }
  2049. present(picker, animated: true, completion: nil)
  2050. case "video":
  2051. var config = PHPickerConfiguration()
  2052. config.filter = .videos
  2053. let picker = PHPickerViewController(configuration: config)
  2054. picker.delegate = self
  2055. if UIBarButtonItem.appearance().titleTextAttributes(for: .normal) != nil {
  2056. isBlackCancelButton = UIBarButtonItem.appearance().titleTextAttributes(for: .normal)?.values.first as! NSObject == UIColor.black
  2057. }
  2058. if !isBlackCancelButton {
  2059. let cancelButtonAttributes = [NSAttributedString.Key.foregroundColor: UIColor.black, NSAttributedString.Key.font : UIFont.systemFont(ofSize: 16)]
  2060. UIBarButtonItem.appearance().setTitleTextAttributes(cancelButtonAttributes , for: .normal)
  2061. }
  2062. present(picker, animated: true, completion: nil)
  2063. case "imageCamera":
  2064. if isContactCenter && channelContactCenter == "2" {
  2065. self.view.makeToast("You can't take photo when Video Call".localized(), duration: 3)
  2066. return
  2067. }
  2068. imageVideoPicker.present(source: .imageCamera)
  2069. case "videoCamera":
  2070. if isContactCenter && channelContactCenter == "2" {
  2071. self.view.makeToast("You can't take video when Video Call".localized(), duration: 3)
  2072. return
  2073. }
  2074. imageVideoPicker.present(source: .videoCamera)
  2075. default:
  2076. break
  2077. }
  2078. }
  2079. }
  2080. @IBAction func photoTapped(_ sender: UIButton) {
  2081. if (self.constraintBottomAttachment.constant != 0.0) {
  2082. constraintBottomAttachment.constant = 0.0
  2083. self.viewSticker.removeConstraints(self.viewSticker.constraints)
  2084. self.viewSticker.removeFromSuperview()
  2085. }
  2086. if isContactCenter && fPinContacCenter.isEmpty && isRequestContactCenter {
  2087. return
  2088. }
  2089. let alertController = LibAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
  2090. if let action = self.actionImageVideo(for: "imageCamera", title: "Take Photo".localized()) {
  2091. alertController.addAction(action)
  2092. }
  2093. if let action = self.actionImageVideo(for: "videoCamera", title: "Take Video".localized()) {
  2094. alertController.addAction(action)
  2095. }
  2096. alertController.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: nil))
  2097. self.present(alertController, animated: true)
  2098. }
  2099. @IBAction func stickerTapped(_ sender: UIButton) {
  2100. if isContactCenter && fPinContacCenter.isEmpty && isRequestContactCenter {
  2101. return
  2102. }
  2103. DispatchQueue.main.async {
  2104. if (self.constraintBottomAttachment.constant == 0.0) {
  2105. self.constraintBottomAttachment.constant = 200.0
  2106. self.view.addSubview(self.viewSticker)
  2107. self.viewSticker.translatesAutoresizingMaskIntoConstraints = false
  2108. NSLayoutConstraint.activate([
  2109. self.viewSticker.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
  2110. self.viewSticker.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
  2111. self.viewSticker.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
  2112. self.viewSticker.heightAnchor.constraint(equalToConstant: 200)
  2113. ])
  2114. let layout = UICollectionViewFlowLayout()
  2115. layout.scrollDirection = .vertical
  2116. let collectionSticker = UICollectionView(frame: .zero, collectionViewLayout: layout)
  2117. collectionSticker.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cellSticker")
  2118. collectionSticker.delegate = self
  2119. collectionSticker.dataSource = self
  2120. collectionSticker.backgroundColor = .clear
  2121. self.viewSticker.addSubview(collectionSticker)
  2122. collectionSticker.translatesAutoresizingMaskIntoConstraints = false
  2123. NSLayoutConstraint.activate([
  2124. collectionSticker.topAnchor.constraint(equalTo: self.viewSticker.topAnchor, constant: 20),
  2125. collectionSticker.bottomAnchor.constraint(equalTo: self.viewSticker.bottomAnchor, constant: -20),
  2126. collectionSticker.leadingAnchor.constraint(equalTo: self.viewSticker.leadingAnchor, constant: 20),
  2127. collectionSticker.trailingAnchor.constraint(equalTo: self.viewSticker.trailingAnchor, constant: -20)
  2128. ])
  2129. if (self.currentIndexpath != nil) {
  2130. DispatchQueue.main.async {
  2131. self.tableChatView.scrollToRow(at: IndexPath(row: self.currentIndexpath!.row, section: self.currentIndexpath!.section), at: .none, animated: false)
  2132. }
  2133. } else {
  2134. self.tableChatView.scrollToBottom()
  2135. }
  2136. } else {
  2137. self.constraintBottomAttachment.constant = 0.0
  2138. self.viewSticker.removeConstraints(self.viewSticker.constraints)
  2139. self.viewSticker.removeFromSuperview()
  2140. }
  2141. }
  2142. }
  2143. @IBAction func fileTapped(_ sender: UIButton) {
  2144. if isContactCenter && fPinContacCenter.isEmpty && isRequestContactCenter {
  2145. return
  2146. }
  2147. if (self.constraintBottomAttachment.constant != 0.0) {
  2148. constraintBottomAttachment.constant = 0.0
  2149. self.viewSticker.removeConstraints(self.viewSticker.constraints)
  2150. self.viewSticker.removeFromSuperview()
  2151. }
  2152. documentPicker.present()
  2153. }
  2154. @objc func sendTapped() {
  2155. sendChat(message_text: textFieldSend.text!, viewController: self)
  2156. }
  2157. @objc func showChooserACKConfidential() {
  2158. let alertController = LibAlertController(title: "Message Mode".localized(), message: "Select".localized() + " " + "Message Mode".localized(), preferredStyle: .actionSheet)
  2159. let imageConfidential = resizeImage(image: UIImage(named: "pb_icon_conf_msg_on", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, targetSize: CGSize(width: 30, height: 30)).withRenderingMode(.alwaysOriginal)
  2160. let imageAck = resizeImage(image: UIImage(named: "pb_icon_ack_msg_on", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, targetSize: CGSize(width: 30, height: 30)).withRenderingMode(.alwaysOriginal)
  2161. let imageSecret = resizeImage(image: UIImage(named: "pb_icon_secret_msg_on", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, targetSize: CGSize(width: 30, height: 30)).withRenderingMode(.alwaysOriginal)
  2162. let confidentialAction = UIAlertAction(title: "Confidential Message".localized(), style: .default, handler: { (UIAlertAction) in
  2163. self.isConfidential = !self.isConfidential
  2164. if self.isConfidential {
  2165. self.buttonAckConfidential.setImage(imageConfidential, for: .normal)
  2166. } else {
  2167. self.buttonAckConfidential.setImage(UIImage(systemName: "gearshape.fill", withConfiguration: UIImage.SymbolConfiguration(scale: .large))?.withTintColor(.white).withRenderingMode(.alwaysTemplate), for: .normal)
  2168. }
  2169. if self.isAck {
  2170. self.isAck = false
  2171. }
  2172. if self.isSecret {
  2173. self.isSecret = false
  2174. }
  2175. })
  2176. let ackAction = UIAlertAction(title: "Confirmation Message".localized(), style: .default, handler: { (UIAlertAction) in
  2177. self.isAck = !self.isAck
  2178. if self.isAck {
  2179. self.buttonAckConfidential.setImage(imageAck, for: .normal)
  2180. } else {
  2181. self.buttonAckConfidential.setImage(UIImage(systemName: "gearshape.fill", withConfiguration: UIImage.SymbolConfiguration(scale: .large))?.withTintColor(.white).withRenderingMode(.alwaysTemplate), for: .normal)
  2182. }
  2183. if self.isConfidential {
  2184. self.isConfidential = false
  2185. }
  2186. if self.isSecret {
  2187. self.isSecret = false
  2188. }
  2189. })
  2190. let secretAction = UIAlertAction(title: "Secret Message".localized(), style: .default, handler: { (UIAlertAction) in
  2191. self.isSecret = !self.isSecret
  2192. if self.isSecret {
  2193. self.buttonAckConfidential.setImage(imageSecret, for: .normal)
  2194. } else {
  2195. self.buttonAckConfidential.setImage(UIImage(systemName: "gearshape.fill", withConfiguration: UIImage.SymbolConfiguration(scale: .large))?.withTintColor(.white).withRenderingMode(.alwaysTemplate), for: .normal)
  2196. }
  2197. if self.isConfidential {
  2198. self.isConfidential = false
  2199. }
  2200. if self.isAck {
  2201. self.isAck = false
  2202. }
  2203. })
  2204. confidentialAction.setValue(imageConfidential, forKey: "image")
  2205. ackAction.setValue(imageAck, forKey: "image")
  2206. secretAction.setValue(imageSecret, forKey: "image")
  2207. alertController.addAction(confidentialAction)
  2208. alertController.addAction(ackAction)
  2209. alertController.addAction(secretAction)
  2210. alertController.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: { (UIAlertAction) in
  2211. self.isConfidential = false
  2212. self.isAck = false
  2213. self.isSecret = false
  2214. self.buttonAckConfidential.setImage(UIImage(systemName: "gearshape.fill", withConfiguration: UIImage.SymbolConfiguration(scale: .large))?.withTintColor(.white).withRenderingMode(.alwaysTemplate), for: .normal)
  2215. }))
  2216. self.present(alertController, animated: true, completion: nil)
  2217. }
  2218. public func setAckConfidential(isAck: Bool, isConfidential: Bool) {
  2219. self.isConfidential = isConfidential
  2220. self.isAck = isAck
  2221. let imageConfidential = resizeImage(image: UIImage(named: "confidential_icon", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, targetSize: CGSize(width: 30, height: 30)).withRenderingMode(.alwaysOriginal)
  2222. let imageAck = resizeImage(image: UIImage(named: "ack_icon", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, targetSize: CGSize(width: 30, height: 30)).withRenderingMode(.alwaysOriginal)
  2223. if isAck {
  2224. buttonAckConfidential.setImage(imageAck, for: .normal)
  2225. } else if isConfidential {
  2226. buttonAckConfidential.setImage(imageConfidential, for: .normal)
  2227. } else {
  2228. self.buttonAckConfidential.setImage(UIImage(systemName: "gearshape.fill", withConfiguration: UIImage.SymbolConfiguration(scale: .large))?.withTintColor(.white).withRenderingMode(.alwaysTemplate), for: .normal)
  2229. }
  2230. }
  2231. @objc func addRoom(sender: UIBarButtonItem) {
  2232. let controller = QmeraCallContactViewController()
  2233. controller.isDismiss = { user in
  2234. DispatchQueue.global().async {
  2235. _ = Nexilis.write(message: CoreMessage_TMessageBank.getCCRoomInvite(l_pin: user.pin, ticket_id: self.complaintId, channel: self.channelContactCenter))
  2236. }
  2237. }
  2238. controller.selectedUser.append(contentsOf: users)
  2239. controller.isInviteCC = true
  2240. self.navigationController?.show(controller, sender: nil)
  2241. }
  2242. @objc func audioVideoCall(sender: UIBarButtonItem) {
  2243. if sender.tag == 0 {
  2244. if !Nexilis.checkingAccess(key: "audio_call") {
  2245. self.view.makeToast("Feature disabled..".localized(), duration: 3)
  2246. return
  2247. }
  2248. let goAudioCall = Nexilis.checkMicPermission()
  2249. if !goAudioCall{
  2250. let alert = LibAlertController(title: "Attention!".localized(), message: "Please allow microphone permission in your settings".localized(), preferredStyle: .alert)
  2251. alert.addAction(UIAlertAction(title: "OK".localized(), style: UIAlertAction.Style.default, handler: {_ in
  2252. if let url = URL(string: UIApplication.openSettingsURLString), UIApplication.shared.canOpenURL(url) {
  2253. UIApplication.shared.open(url, options: [:], completionHandler: nil)
  2254. }
  2255. }))
  2256. self.navigationController?.present(alert, animated: true, completion: nil)
  2257. return
  2258. }
  2259. if let pin = dataPerson["f_pin"] {
  2260. if !CheckConnection.isConnectedToNetwork() {
  2261. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  2262. imageView.tintColor = .white
  2263. let banner = FloatingNotificationBanner(title: "Check your connection".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
  2264. banner.show()
  2265. return
  2266. }
  2267. let controller = QmeraAudioViewController()
  2268. controller.user = User.getData(pin: pin)
  2269. controller.isOutgoing = true
  2270. controller.modalPresentationStyle = .overCurrentContext
  2271. present(controller, animated: true, completion: nil)
  2272. }
  2273. } else {
  2274. if !Nexilis.checkingAccess(key: "video_call") {
  2275. self.view.makeToast("Feature disabled..".localized(), duration: 3)
  2276. return
  2277. }
  2278. let goAudioCall = Nexilis.checkMicPermission()
  2279. let goVideoCall = Nexilis.checkCameraPermission()
  2280. if goVideoCall == 0 {
  2281. let alert = LibAlertController(title: "Attention!".localized(), message: !goAudioCall && goVideoCall == 0 ? "Please allow microphone & camera permission in your settings".localized() : !goAudioCall ? "Please allow microphone permission in your settings".localized() : "Please allow camera permission in your settings", preferredStyle: .alert)
  2282. alert.addAction(UIAlertAction(title: "OK".localized(), style: UIAlertAction.Style.default, handler: {_ in
  2283. if let url = URL(string: UIApplication.openSettingsURLString), UIApplication.shared.canOpenURL(url) {
  2284. UIApplication.shared.open(url, options: [:], completionHandler: nil)
  2285. }
  2286. }))
  2287. self.navigationController?.present(alert, animated: true, completion: nil)
  2288. return
  2289. } else if goVideoCall == -1 {
  2290. return
  2291. }
  2292. if !CheckConnection.isConnectedToNetwork() {
  2293. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  2294. imageView.tintColor = .white
  2295. let banner = FloatingNotificationBanner(title: "Check your connection".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
  2296. banner.show()
  2297. return
  2298. }
  2299. let videoVC = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "videoVCQmera") as! QmeraVideoViewController
  2300. videoVC.dataPerson.append(dataPerson)
  2301. self.show(videoVC, sender: nil)
  2302. }
  2303. }
  2304. @objc func dismissKeyboard() {
  2305. if isSearching {
  2306. searchBar.resignFirstResponder()
  2307. } else {
  2308. textFieldSend.resignFirstResponder() // dismiss keyoard
  2309. if (self.constraintBottomAttachment.constant != 0.0) {
  2310. constraintBottomAttachment.constant = 0.0
  2311. self.viewSticker.removeConstraints(self.viewSticker.constraints)
  2312. self.viewSticker.removeFromSuperview()
  2313. }
  2314. }
  2315. }
  2316. @objc func didTapExit() {
  2317. if complaintId.isEmpty || fromVCAC {
  2318. self.dismiss(animated: true, completion: {
  2319. self.removeAllObjectBeforeDismissVC()
  2320. })
  2321. } else if !complaintId.isEmpty {
  2322. let alert = LibAlertController(title: "Interaction with Call Center is in progress".localized(), message: "Are you sure you want to end the Call Center?".localized(), preferredStyle: .alert)
  2323. alert.addAction(UIAlertAction(title: "No".localized(), style: UIAlertAction.Style.default, handler: nil))
  2324. alert.addAction(UIAlertAction(title: "Yes".localized(), style: UIAlertAction.Style.default, handler: {(_) in
  2325. self.endCallCenter()
  2326. }))
  2327. self.present(alert, animated: true, completion: nil)
  2328. }
  2329. }
  2330. private func removeAllObjectBeforeDismissVC() {
  2331. for timer in self.timerCredential.values {
  2332. timer.invalidate()
  2333. }
  2334. self.timeoutCC.invalidate()
  2335. SecureUserDefaults.shared.removeValue(forKey: "inEditorPersonal")
  2336. NotificationCenter.default.removeObserver(self)
  2337. self.removeFromParent()
  2338. if !self.isContactCenter {
  2339. let l_pin = self.dataPerson["f_pin"]!!
  2340. SecureUserDefaults.shared.set("\(self.textFieldSend.textColor != UIColor.lightGray ? self.textFieldSend.text! : ""),\(self.reffId ?? "")", forKey: "saved_\(l_pin)")
  2341. }
  2342. }
  2343. public func endCallCenter() {
  2344. timeoutCC.invalidate()
  2345. let complaintId = self.complaintId
  2346. let idMe = User.getMyPin()!
  2347. let onGoingCC: String = SecureUserDefaults.shared.value(forKey: "onGoingCC") ?? ""
  2348. let requester = onGoingCC.components(separatedBy: ",")[0]
  2349. let officer = onGoingCC.isEmpty ? "" : onGoingCC.components(separatedBy: ",")[1]
  2350. DispatchQueue.global().async {
  2351. let date = "\(Date().currentTimeMillis())"
  2352. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  2353. do {
  2354. _ = try Database.shared.insertRecord(fmdb: fmdb, table: "CALL_CENTER_HISTORY", cvalues: [
  2355. "type" : self.channelContactCenter,
  2356. "title" : "Contact Center".localized(),
  2357. "time" : self.dateStartCC,
  2358. "f_pin" : officer,
  2359. "data" : self.complaintId,
  2360. "time_end" : date,
  2361. "complaint_id" : self.complaintId,
  2362. "members" : "",
  2363. "requester": requester
  2364. ], replace: true)
  2365. } catch {
  2366. rollback.pointee = true
  2367. print("Access database error: \(error.localizedDescription)")
  2368. }
  2369. })
  2370. if officer == idMe {
  2371. _ = Nexilis.write(message: CoreMessage_TMessageBank.endCallCenter(complaint_id: complaintId, l_pin: requester))
  2372. } else {
  2373. if requester == idMe {
  2374. _ = Nexilis.write(message: CoreMessage_TMessageBank.endCallCenter(complaint_id: complaintId, l_pin: officer))
  2375. } else {
  2376. _ = Nexilis.write(message: CoreMessage_TMessageBank.leaveCCRoomInvite(ticket_id: complaintId))
  2377. }
  2378. }
  2379. SecureUserDefaults.shared.removeValue(forKey: "onGoingCC")
  2380. SecureUserDefaults.shared.removeValue(forKey: "membersCC")
  2381. SecureUserDefaults.shared.removeValue(forKey: "waitingRequestCC")
  2382. }
  2383. self.dismiss(animated: true, completion: {
  2384. self.removeAllObjectBeforeDismissVC()
  2385. })
  2386. }
  2387. @objc func keyboardWillHide(notification: NSNotification) {
  2388. if self.viewIfLoaded?.window != nil && !isEditingMessage {
  2389. let info:NSDictionary = notification.userInfo! as NSDictionary
  2390. let duration: CGFloat = info[UIResponder.keyboardAnimationDurationUserInfoKey] as! NSNumber as! CGFloat
  2391. self.constraintViewTextField.constant = 0
  2392. self.constraintBottomContainerMultpileSelectSession.constant = 0
  2393. UIView.animate(withDuration: TimeInterval(duration), animations: {
  2394. self.view.layoutIfNeeded()
  2395. })
  2396. }
  2397. }
  2398. @objc func keyboardWillShow(notification: NSNotification) {
  2399. if self.viewIfLoaded?.window != nil && !isEditingMessage {
  2400. if (self.constraintBottomAttachment.constant != 0.0) {
  2401. self.constraintBottomAttachment.constant = 0.0
  2402. self.viewSticker.removeConstraints(self.viewSticker.constraints)
  2403. self.viewSticker.removeFromSuperview()
  2404. }
  2405. let info:NSDictionary = notification.userInfo! as NSDictionary
  2406. let keyboardSize = (info[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
  2407. let keyboardHeight: CGFloat = keyboardSize.height
  2408. let duration: CGFloat = info[UIResponder.keyboardAnimationDurationUserInfoKey] as! NSNumber as! CGFloat
  2409. if self.constraintViewTextField.constant != keyboardHeight - 60 {
  2410. self.constraintViewTextField.constant = keyboardHeight - 60
  2411. if isSearching {
  2412. self.constraintViewTextField.constant = self.constraintViewTextField.constant + 60
  2413. self.constraintBottomContainerMultpileSelectSession.constant = -keyboardHeight
  2414. }
  2415. UIView.animate(withDuration: TimeInterval(duration), animations: {
  2416. self.view.layoutIfNeeded()
  2417. })
  2418. if isSearching {
  2419. self.tableChatView.scrollToBottom()
  2420. } else {
  2421. if (self.currentIndexpath != nil) {
  2422. self.tableChatView.scrollToRow(at: IndexPath(row: self.currentIndexpath!.row, section: self.currentIndexpath!.section), at: .none, animated: false)
  2423. } else {
  2424. self.tableChatView.scrollToBottom()
  2425. }
  2426. }
  2427. }
  2428. } else if isEditingMessage {
  2429. let info:NSDictionary = notification.userInfo! as NSDictionary
  2430. let keyboardSize = (info[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
  2431. let keyboardHeight: CGFloat = keyboardSize.height
  2432. let duration: CGFloat = info[UIResponder.keyboardAnimationDurationUserInfoKey] as! NSNumber as! CGFloat
  2433. let constant: CGFloat = 0 - keyboardHeight - 15
  2434. constraintBottomeditTextView.constant = constant
  2435. constraintBottomSendEditTV.constant = constant
  2436. UIView.animate(withDuration: TimeInterval(duration), animations: {
  2437. self.view.layoutIfNeeded()
  2438. })
  2439. }
  2440. }
  2441. private func sendChat(message_scope_id:String = "3", status:String = "1", message_text:String = "", credential:String = "0", attachment_flag: String = "0", ex_blog_id: String = "", message_large_text: String = "", ex_format: String = "", image_id: String = "", audio_id: String = "", video_id: String = "", file_id: String = "", thumb_id: String = "", reff_id: String = "", read_receipts: String = "4", chat_id: String = "", is_call_center: String = "0", call_center_id: String = "", viewController: UIViewController, isAutoSendCC : Bool = false, gif_id: String = "", is_forwarded: Int = 0, is_secret: Int = 0) {
  2442. if viewController is EditorPersonal && file_id == "" && dataMessageForward == nil && !isAutoSendCC{
  2443. if ((textFieldSend.text!.trimmingCharacters(in: .whitespacesAndNewlines) == "Send message".localized() && textFieldSend.textColor == UIColor.lightGray && attachment_flag != "11") || textFieldSend.text!.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ) {
  2444. dismissKeyboard()
  2445. viewController.view.makeToast("Write Messages".localized(), duration: 3)
  2446. if (textFieldSend.text!.trimmingCharacters(in: .whitespacesAndNewlines) != "Send message".localized()) {
  2447. textFieldSend.text = ""
  2448. }
  2449. if (self.heightTextFieldSend.constant != 40) {
  2450. self.heightTextFieldSend.constant = 40
  2451. }
  2452. return
  2453. }
  2454. }
  2455. var reff_id = reff_id
  2456. if (reffId != nil) {
  2457. reff_id = reffId!
  2458. }
  2459. var is_call_center = is_call_center
  2460. var call_center_id = call_center_id
  2461. var l_pin = dataPerson["f_pin"]!!
  2462. var message_scope_id = message_scope_id
  2463. var chat_id = chat_id
  2464. if (isContactCenter) {
  2465. if fPinContacCenter.isEmpty && isRequestContactCenter {
  2466. if textFieldSend.text!.trimmingCharacters(in: .whitespacesAndNewlines) != "Send message".localized() && textFieldSend.textColor != UIColor.lightGray && constraintViewTextField.constant == 0 {
  2467. textFieldSend.text = "Send message".localized()
  2468. textFieldSend.textColor = UIColor.lightGray
  2469. } else if constraintViewTextField.constant != 0 {
  2470. textFieldSend.text = ""
  2471. }
  2472. dismissKeyboard()
  2473. self.view.makeToast("Unable to send message. Waiting for the officer to accept your request".localized(), duration: 3)
  2474. return
  2475. }
  2476. is_call_center = "1"
  2477. call_center_id = complaintId
  2478. l_pin = fPinContacCenter
  2479. message_scope_id = "5"
  2480. chat_id = complaintId
  2481. if isAutoSendCC {
  2482. timeoutCC = Timer.scheduledTimer(withTimeInterval: 30.0, repeats: false, block: {_ in
  2483. let imageView = UIImageView(image: UIImage(systemName: "info.circle"))
  2484. imageView.tintColor = .white
  2485. let banner = FloatingNotificationBanner(title: "Customer doesn't respond in 30 second, so call center session will be ended automatically.".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .info, colors: nil, iconPosition: .center)
  2486. banner.show()
  2487. self.endCallCenter()
  2488. })
  2489. }
  2490. }
  2491. let message_text = message_text.trimmingCharacters(in: .whitespacesAndNewlines)
  2492. let idMe = User.getMyPin() as String?
  2493. var opposite_pin = ""
  2494. if isContactCenter {
  2495. opposite_pin = ""
  2496. } else {
  2497. opposite_pin = idMe ?? ""
  2498. }
  2499. var credential = credential
  2500. if isConfidential {
  2501. credential = "1"
  2502. }
  2503. var read_receipts = read_receipts
  2504. if isAck {
  2505. read_receipts = "8"
  2506. }
  2507. var is_secret = is_secret
  2508. if isSecret {
  2509. is_secret = 1
  2510. }
  2511. sendTyping(l_pin: l_pin, isTyping: true)
  2512. let message = CoreMessage_TMessageBank.sendMessage(l_pin: l_pin, message_scope_id: message_scope_id, status: status, message_text: message_text, credential: credential, attachment_flag: attachment_flag, ex_blog_id: ex_blog_id, message_large_text: message_large_text, ex_format: ex_format, image_id: image_id, audio_id: audio_id, video_id: video_id, file_id: file_id, thumb_id: thumb_id, reff_id: reff_id, read_receipts: read_receipts, chat_id: chat_id, is_call_center: is_call_center, call_center_id: call_center_id, opposite_pin: opposite_pin, gif_id: gif_id, isForwarded: "\(is_forwarded)", isSecret: "\(is_secret)")
  2513. Nexilis.addQueueMessage(message: message)
  2514. let messageId = String(message.mBodies[CoreMessage_TMessageKey.MESSAGE_ID]!)
  2515. if credential == "1" {
  2516. self.listTimerCredential[messageId] = 60
  2517. }
  2518. var row: [String: Any?] = [:]
  2519. row["message_id"] = messageId
  2520. row["f_pin"] = idMe
  2521. row["l_pin"] = dataPerson["f_pin"]!!
  2522. row["message_scope_id"] = message_scope_id
  2523. row["server_date"] = "\(Date().currentTimeMillis())"
  2524. row["status"] = status
  2525. row["message_text"] = message_text
  2526. row["audio_id"] = audio_id
  2527. row["video_id"] = video_id
  2528. row["image_id"] = image_id
  2529. row["thumb_id"] = thumb_id
  2530. row["read_receipts"] = read_receipts
  2531. row["credential"] = credential
  2532. row["chat_id"] = chat_id
  2533. row["file_id"] = file_id
  2534. row["blog_id"] = ex_blog_id
  2535. row["attachment_flag"] = attachment_flag
  2536. row["reff_id"] = reff_id
  2537. row["progress"] = 0.0
  2538. row["lock"] = "0"
  2539. row["is_stared"] = "0"
  2540. row["gif_id"] = gif_id
  2541. row[TypeDataMessage.is_forwarded] = is_forwarded
  2542. row["isSelected"] = false
  2543. row[TypeDataMessage.is_call_center] = is_call_center
  2544. row[TypeDataMessage.call_center_id] = call_center_id
  2545. row[TypeDataMessage.opposite_pin] = opposite_pin
  2546. if !dataDates.contains("Today".localized()) {
  2547. dataDates.append("Today".localized())
  2548. tableChatView.insertSections(IndexSet(integer: dataDates.count - 1), with: .none)
  2549. }
  2550. row["chat_date"] = "Today".localized()
  2551. dataMessages.append(row)
  2552. tableChatView.insertRows(at: [IndexPath(row: dataMessages.filter({ $0["chat_date"] as? String ?? "" == dataDates[dataDates.count - 1]}).count - 1, section: dataDates.count - 1)], with: .none)
  2553. if credential == "1" {
  2554. var timer = Timer()
  2555. var minute = 60
  2556. timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: {_ in
  2557. minute -= 1
  2558. self.listTimerCredential[messageId] = minute
  2559. if minute == 0 {
  2560. timer.invalidate()
  2561. self.listTimerCredential.removeValue(forKey: messageId)
  2562. self.timerCredential.removeValue(forKey: messageId)
  2563. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == messageId})
  2564. if idx != nil {
  2565. self.dataMessages[idx!]["lock"] = "2"
  2566. self.dataMessages[idx!]["reff_id"] = ""
  2567. }
  2568. DispatchQueue.global().async {
  2569. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  2570. do {
  2571. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  2572. "lock" : "2"
  2573. ], _where: "message_id = '\(messageId)'")
  2574. } catch {
  2575. rollback.pointee = true
  2576. print("Access database error: \(error.localizedDescription)")
  2577. }
  2578. })
  2579. }
  2580. }
  2581. let section = self.dataDates.firstIndex(of: self.dataDates[self.dataDates.count - 1])
  2582. let row = self.dataMessages.filter({$0["chat_date"] as? String ?? "" == self.dataDates[self.dataDates.count - 1]}).firstIndex(where: { $0["message_id"] as? String == messageId})
  2583. if row != nil && section != nil{
  2584. self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
  2585. }
  2586. })
  2587. self.timerCredential[messageId] = timer
  2588. }
  2589. if textFieldSend.text!.trimmingCharacters(in: .whitespacesAndNewlines) != "Send message".localized() && textFieldSend.textColor != UIColor.lightGray && constraintViewTextField.constant == 0 {
  2590. textFieldSend.text = "Send message".localized()
  2591. textFieldSend.textColor = UIColor.lightGray
  2592. } else if constraintViewTextField.constant != 0 {
  2593. textFieldSend.text = ""
  2594. heightTextFieldSend.constant = 40
  2595. }
  2596. deleteReplyView()
  2597. deleteLinkPreview()
  2598. NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
  2599. DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
  2600. self.tableChatView.scrollToBottom()
  2601. if self.markerCounter != nil {
  2602. let lastMarkerCounter = self.markerCounter
  2603. self.markerCounter = nil
  2604. self.tableChatView.beginUpdates()
  2605. let indexMessage = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == lastMarkerCounter })
  2606. if indexMessage != nil {
  2607. let section = self.dataDates.firstIndex(of: self.dataMessages[indexMessage!]["chat_date"] as? String ?? "")
  2608. let row = self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == self.dataMessages[indexMessage!]["chat_date"] as? String ?? ""}).firstIndex(where: { $0["message_id"] as? String == self.dataMessages[indexMessage!]["message_id"] as? String })
  2609. if row != nil && section != nil {
  2610. self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
  2611. }
  2612. }
  2613. self.tableChatView.endUpdates()
  2614. }
  2615. }
  2616. // DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
  2617. // self.timerFakeProgress = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
  2618. // self.updateProgress(row as [AnyHashable : Any])
  2619. // if self.fakeProgMultip == self.maxFakeProgMultip {
  2620. // self.timerFakeProgress?.invalidate()
  2621. // self.fakeProgMultip = 0
  2622. // }
  2623. // }
  2624. // }
  2625. }
  2626. @objc func ccAction(sender: UIButton) {
  2627. if self.nowSelectedCategoryCC == "CantReturn" {
  2628. if sender.tag == 503 {
  2629. self.view.makeToast("You can't request Call Center more than one".localized(), duration: 3)
  2630. } else if sender.tag == 504 {
  2631. busyCCAction(sender: sender)
  2632. }
  2633. return
  2634. }
  2635. if self.nowSelectedCategoryCC == "endCC" {
  2636. return
  2637. }
  2638. let id = sender.restorationIdentifier?.components(separatedBy: ",")[0]
  2639. let service_id = sender.restorationIdentifier?.components(separatedBy: ",")[1]
  2640. let level = id!.substring(from: 5, to: 5)
  2641. let levelNow = self.nowSelectedCategoryCC.substring(from: 5, to: 5)
  2642. var isRequest = false
  2643. var channel = 0
  2644. var row: [String: Any?] = [:]
  2645. if nowSelectedCategoryCC.isEmpty || level > levelNow {
  2646. if Utils.getDefaultCC() == "No" && !showToastTwiceClick {
  2647. self.view.makeToast("You can press your choice again to change category".localized(), duration: 3)
  2648. showToastTwiceClick = true
  2649. }
  2650. row["message_id"] = ""
  2651. row["chat_date"] = "Today".localized()
  2652. let dataChat: [CategoryCC] = CategoryCC.getDatafromParent(parent: service_id!)
  2653. if dataChat.count != 0 {
  2654. var data : [CategoryCC] = []
  2655. for i in 0..<dataChat.count {
  2656. data.append(CategoryCC(id: "level\(Int(level)! + 1)_\(i)", service_id: dataChat[i].service_id, service_name: dataChat[i].service_name, parent: id!, description: dataChat[i].description, is_tablet: dataChat[i].is_tablet))
  2657. }
  2658. row["category_cc"] = data
  2659. } else if dataMessages[Int(level)!]["attachment_flag"] == nil {
  2660. let listStringName: [String] = ["Informasi Umum Produk Call 1500046", "Informasi Spesifik Produk"]
  2661. var data : [CategoryCC] = []
  2662. for i in 0..<listStringName.count {
  2663. data.append(CategoryCC(id: "level\(Int(level)! + 1)_\(i)", service_id: service_id!, service_name: listStringName[i], parent: id!, description: "", is_tablet: "0"))
  2664. }
  2665. row["category_cc"] = data
  2666. row["attachment_flag"] = "502"
  2667. } else if dataMessages[Int(level)!]["attachment_flag"] != nil && dataMessages[Int(level)!]["attachment_flag"] as? String ?? "" == "502" {
  2668. if id == "level\(Int(level)!)_0" {
  2669. if let url = URL(string: "tel://1500046") {
  2670. UIApplication.shared.open(url)
  2671. }
  2672. return
  2673. } else {
  2674. let listStringName: [String] = ["Messaging".localized(), "Secure SMS".localized(), "VoIP Call".localized(), "Email".localized(), "Video Call".localized(), "GSM Call".localized(), "GPT Chatbot".localized(), "WhatsApp"]
  2675. // let listStringName: [String] = ["Chat with a Representative".localized(), "Video Call a Representative".localized(), "Call a Representative".localized()]
  2676. var data : [CategoryCC] = []
  2677. let channels : [String] = ["0", "4", "1", "3", "2", "5", "7", "6"]
  2678. for i in 0..<listStringName.count {
  2679. data.append(CategoryCC(id: "level\(Int(level)! + 1)_\(channels[i])", service_id: service_id!, service_name: listStringName[i], parent: id!, description: "", is_tablet: "0"))
  2680. }
  2681. row["category_cc"] = data
  2682. row["attachment_flag"] = "503"
  2683. }
  2684. } else {
  2685. channel = Int((id?.components(separatedBy: "_")[1])!)!
  2686. if channel == 1 || channel == 2 {
  2687. if channel == 2 {
  2688. let goAudioCall = Nexilis.checkMicPermission()
  2689. let goVideoCall = Nexilis.checkCameraPermission()
  2690. if goVideoCall == 0 {
  2691. let alert = LibAlertController(title: "Attention!".localized(), message: !goAudioCall && goVideoCall == 0 && channel == 2 ? "Please allow microphone & camera permission in your settings".localized() : !goAudioCall ? "Please allow microphone permission in your settings".localized() : "Please allow camera permission in your settings", preferredStyle: .alert)
  2692. alert.addAction(UIAlertAction(title: "OK".localized(), style: UIAlertAction.Style.default, handler: {_ in
  2693. if let url = URL(string: UIApplication.openSettingsURLString), UIApplication.shared.canOpenURL(url) {
  2694. UIApplication.shared.open(url, options: [:], completionHandler: nil)
  2695. }
  2696. }))
  2697. self.navigationController?.present(alert, animated: true, completion: nil)
  2698. return
  2699. } else if goVideoCall == -1 {
  2700. return
  2701. }
  2702. } else if channel == 1 {
  2703. let goAudioCall = Nexilis.checkMicPermission()
  2704. if !goAudioCall{
  2705. let alert = LibAlertController(title: "Attention!".localized(), message: "Please allow microphone permission in your settings".localized(), preferredStyle: .alert)
  2706. alert.addAction(UIAlertAction(title: "OK".localized(), style: UIAlertAction.Style.default, handler: {_ in
  2707. if let url = URL(string: UIApplication.openSettingsURLString), UIApplication.shared.canOpenURL(url) {
  2708. UIApplication.shared.open(url, options: [:], completionHandler: nil)
  2709. }
  2710. }))
  2711. self.navigationController?.present(alert, animated: true, completion: nil)
  2712. return
  2713. }
  2714. }
  2715. } else if channel == 3 {
  2716. requestEmailContactCenter(channel)
  2717. return
  2718. } else if channel == 4 {
  2719. requestSMSContactCenter(channel)
  2720. return
  2721. } else if channel == 5 {
  2722. requestGSMCallContactCenter(channel)
  2723. return
  2724. } else if channel == 6 {
  2725. requestWhatsappContactCenter(channel)
  2726. return
  2727. } else if channel == 7 {
  2728. APIS.openSmartChatbot()
  2729. return
  2730. }
  2731. row["category_cc"] = "Please wait while we connect you\nto one of our service representatives".localized()
  2732. isRequest = true
  2733. }
  2734. if dataMessages[Int(level)!]["attachment_flag"] == nil || dataMessages[Int(level)!]["attachment_flag"] as? String ?? "" != "503" {
  2735. dataMessages.append(row)
  2736. self.nowSelectedCategoryCC = id!
  2737. tableChatView.insertRows(at: [IndexPath(row: dataMessages.count - 1, section: 0)], with: .none)
  2738. }
  2739. } else {
  2740. if id == self.nowSelectedCategoryCC {
  2741. if level == "0" {
  2742. self.nowSelectedCategoryCC = ""
  2743. } else {
  2744. let categoryCC = dataMessages[dataMessages.count - 2]["category_cc"] as! [CategoryCC]
  2745. self.nowSelectedCategoryCC = categoryCC[0].parent
  2746. }
  2747. tableChatView.beginUpdates()
  2748. tableChatView.deleteRows(at: [IndexPath(row: dataMessages.count - 1, section: 0)], with: .none)
  2749. dataMessages.remove(at: dataMessages.count - 1)
  2750. tableChatView.endUpdates()
  2751. } else {
  2752. return
  2753. }
  2754. }
  2755. if Utils.getDefaultCC() == "No" {
  2756. if sender.backgroundColor != .orangeBNI {
  2757. var button = dataMessages[dataMessages.count - 2]["category_cc"] as! [CategoryCC]
  2758. if dataMessages[Int(level)!]["attachment_flag"] != nil && dataMessages[Int(level)!]["attachment_flag"] as? String ?? "" == "503" {
  2759. button = dataMessages[dataMessages.count - 1]["category_cc"] as! [CategoryCC]
  2760. }
  2761. for i in button {
  2762. if i.id == id! {
  2763. i.isActive = true
  2764. break
  2765. }
  2766. }
  2767. sender.backgroundColor = .orangeBNI
  2768. dataMessages[dataMessages.count - 2]["category_cc"] = button
  2769. } else {
  2770. let button = dataMessages[dataMessages.count - 1]["category_cc"] as! [CategoryCC]
  2771. for i in button {
  2772. if i.id == id! {
  2773. i.isActive = false
  2774. break
  2775. }
  2776. }
  2777. sender.backgroundColor = .clear
  2778. dataMessages[dataMessages.count - 1]["category_cc"] = button
  2779. }
  2780. }
  2781. if isRequest {
  2782. requestContactCenter(channel: channel, service_id: service_id!, row: row)
  2783. } else {
  2784. self.tableChatView.scrollToBottom()
  2785. }
  2786. }
  2787. private func directCC() {
  2788. if channelContactCenter == "1" || channelContactCenter == "2" {
  2789. if channelContactCenter == "2" {
  2790. let goAudioCall = Nexilis.checkMicPermission()
  2791. let goVideoCall = Nexilis.checkCameraPermission()
  2792. if goVideoCall == 0 {
  2793. let alert = LibAlertController(title: "Attention!".localized(), message: !goAudioCall && goVideoCall == 0 && channelContactCenter == "2" ? "Please allow microphone & camera permission in your settings".localized() : !goAudioCall ? "Please allow microphone permission in your settings".localized() : "Please allow camera permission in your settings", preferredStyle: .alert)
  2794. alert.addAction(UIAlertAction(title: "OK".localized(), style: UIAlertAction.Style.default, handler: {_ in
  2795. if let url = URL(string: UIApplication.openSettingsURLString), UIApplication.shared.canOpenURL(url) {
  2796. UIApplication.shared.open(url, options: [:], completionHandler: nil)
  2797. }
  2798. }))
  2799. self.navigationController?.present(alert, animated: true, completion: nil)
  2800. return
  2801. } else if goVideoCall == -1 {
  2802. return
  2803. }
  2804. } else if channelContactCenter == "1" {
  2805. let goAudioCall = Nexilis.checkMicPermission()
  2806. if !goAudioCall{
  2807. let alert = LibAlertController(title: "Attention!".localized(), message: "Please allow microphone permission in your settings".localized(), preferredStyle: .alert)
  2808. alert.addAction(UIAlertAction(title: "OK".localized(), style: UIAlertAction.Style.default, handler: {_ in
  2809. if let url = URL(string: UIApplication.openSettingsURLString), UIApplication.shared.canOpenURL(url) {
  2810. UIApplication.shared.open(url, options: [:], completionHandler: nil)
  2811. }
  2812. }))
  2813. self.navigationController?.present(alert, animated: true, completion: nil)
  2814. return
  2815. }
  2816. }
  2817. }
  2818. var row: [String: Any?] = [:]
  2819. row["message_id"] = ""
  2820. row["chat_date"] = "Today".localized()
  2821. row["category_cc"] = "Please wait while we connect you\nto one of our service representatives".localized()
  2822. dataMessages.append(row)
  2823. nowSelectedCategoryCC = "CantReturn"
  2824. tableChatView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .none)
  2825. requestContactCenter(channel: Int(channelContactCenter)!, service_id: serviceIdCC, row: row)
  2826. }
  2827. @objc func busyCCAction(sender: UIButton) {
  2828. let id = sender.restorationIdentifier?.components(separatedBy: ",")[0]
  2829. let service_id = sender.restorationIdentifier?.components(separatedBy: ",")[1]
  2830. let level = id!.substring(from: 5, to: 5)
  2831. var row: [String: Any?] = [:]
  2832. if id == "level\(Int(level)!)_0" {
  2833. SecureUserDefaults.shared.set(true, forKey: "waitingRequestCC")
  2834. DispatchQueue.global().async {
  2835. let message = CoreMessage_TMessageBank.getQueuingCallCenter(p_channel: Int(self.channelContactCenter)!)
  2836. message.mBodies[CoreMessage_TMessageKey.CATEGORY_ID] = "\(service_id!)"
  2837. _ = Nexilis.writeSync(message: message, timeout: 30 * 1000)
  2838. }
  2839. row["category_cc"] = "Thank you for contacting us,\none of our officers will contact you soon".localized()
  2840. } else {
  2841. row["category_cc"] = "Thank you for being awesome,\nhave a great day!".localized()
  2842. }
  2843. row["message_id"] = ""
  2844. row["chat_date"] = "Today".localized()
  2845. self.nowSelectedCategoryCC = "endCC"
  2846. dataMessages.append(row)
  2847. tableChatView.insertRows(at: [IndexPath(row: Int(level)!, section: 0)], with: .none)
  2848. self.tableChatView.scrollToBottom()
  2849. // DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {
  2850. // self.dismiss(animated: true)
  2851. // })
  2852. }
  2853. func requestEmailContactCenter(_ channel: Int){
  2854. let idMe = User.getMyPin() as String?
  2855. let complaintId = "CMP_\(idMe!)_\(String(Date().currentTimeMillis()))EML"
  2856. let message = CoreMessage_TMessageBank.getRequestEmailCallCenter(p_channel: channel)
  2857. if let response = Nexilis.writeSync(message: message) {
  2858. if (response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "00") {
  2859. DispatchQueue.main.async {
  2860. let email = response.getBody(key: CoreMessage_TMessageKey.EMAIL, default_value: "")
  2861. let officer = response.getBody(key: CoreMessage_TMessageKey.L_PIN, default_value: "")
  2862. if email.isEmpty {
  2863. self.view.makeToast("Invalid Email Address".localized(), duration: 3)
  2864. return
  2865. }
  2866. // TODO: check if mail available
  2867. Nexilis.openmailAction(to: email)
  2868. }
  2869. }
  2870. }
  2871. }
  2872. func requestSMSContactCenter(_ channel: Int){
  2873. // let idMe = User.getMyPin()!
  2874. // let complaintId = "CMP_\(idMe)_\(String(Date().currentTimeMillis()))SMS"
  2875. // isRequestContactCenter = true
  2876. var phone = Utils.getSMSCenter()
  2877. if phone.substring(from: 0, to: 0) == "0" {
  2878. phone = "+62" + phone.substring(from: 1, to: phone.count)
  2879. }
  2880. APIS.sendSMS(phoneNumber: phone)
  2881. // let tmessage = TMessage()
  2882. // tmessage.mCode = CoreMessage_TMessageCode.ACCEPT_CALL_CENTER
  2883. // tmessage.mStatus = CoreMessage_TMessageUtil.getTID()
  2884. // tmessage.mPIN = idMe
  2885. // tmessage.mBodies[CoreMessage_TMessageKey.F_PIN] = me
  2886. // tmessage.mBodies[CoreMessage_TMessageKey.UPLINE_PIN] = me
  2887. // tmessage.mBodies[CoreMessage_TMessageKey.CHANNEL] = channel
  2888. // tmessage.mBodies[CoreMessage_TMessageKey.CALL_CENTER_ID] = complaint_id
  2889. }
  2890. func requestGSMCallContactCenter(_ channel: Int){
  2891. var phone = Utils.getCallCenter()
  2892. if phone.substring(from: 0, to: 0) == "0" {
  2893. phone = "+62" + phone.substring(from: 1, to: phone.count)
  2894. }
  2895. if let url = URL(string: "tel://\(phone)") {
  2896. UIApplication.shared.open(url)
  2897. }
  2898. }
  2899. func requestWhatsappContactCenter(_ channel: Int){
  2900. var phone = Utils.getWhatsappCenter()
  2901. if phone.substring(from: 0, to: 0) == "0" {
  2902. phone = "+62" + phone.substring(from: 1, to: phone.count)
  2903. }
  2904. APIS.sendWhatsapp(phoneNumber: phone)
  2905. }
  2906. func requestContactCenter(channel: Int, service_id: String, row: [String: Any?]) {
  2907. if !CheckConnection.isConnectedToNetwork() || API.nGetCLXConnState() == 0 {
  2908. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  2909. imageView.tintColor = .white
  2910. let banner = FloatingNotificationBanner(title: "Check your connection".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
  2911. banner.show()
  2912. return
  2913. }
  2914. DispatchQueue.global().async {
  2915. let message = CoreMessage_TMessageBank.getRequestCallCenter(p_channel: channel, category_id: service_id)
  2916. if let response = Nexilis.writeSync(message: message) {
  2917. if !self.isDirectCC {
  2918. DispatchQueue.main.async {
  2919. self.dataMessages.append(row)
  2920. self.nowSelectedCategoryCC = "CantReturn"
  2921. self.tableChatView.insertRows(at: [IndexPath(row: self.dataMessages.count - 1, section: 0)], with: .none)
  2922. self.tableChatView.scrollToBottom()
  2923. }
  2924. }
  2925. if (response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "00") {
  2926. DispatchQueue.main.async {
  2927. SecureUserDefaults.shared.set(true, forKey: "waitingRequestCC")
  2928. let data = response.getBody(key: CoreMessage_TMessageKey.DATA, default_value: "")
  2929. if data.isEmpty {
  2930. SecureUserDefaults.shared.removeValue(forKey: "waitingRequestCC")
  2931. var row: [String: Any?] = [:]
  2932. row["message_id"] = ""
  2933. row["chat_date"] = "Today".localized()
  2934. row["attachment_flag"] = "504"
  2935. let listStringName: [String] = ["Yes".localized(), "No".localized()]
  2936. var data : [CategoryCC] = []
  2937. for i in 0..<listStringName.count {
  2938. data.append(CategoryCC(id: "level\(self.dataMessages.count + 1)_\(i)", service_id: service_id, service_name: listStringName[i], parent: "", description: "", is_tablet: "0"))
  2939. }
  2940. row["category_cc"] = data
  2941. self.dataMessages.append(row)
  2942. self.channelContactCenter = "\(channel)"
  2943. self.tableChatView.insertRows(at: [IndexPath(row: self.dataMessages.count - 1, section: 0)], with: .none)
  2944. self.tableChatView.scrollToBottom()
  2945. } else {
  2946. self.fPinContacCenter = data
  2947. }
  2948. }
  2949. } else {
  2950. DispatchQueue.main.async {
  2951. SecureUserDefaults.shared.removeValue(forKey: "waitingRequestCC")
  2952. var row: [String: Any?] = [:]
  2953. row["message_id"] = ""
  2954. row["chat_date"] = "Today".localized()
  2955. row["attachment_flag"] = "504"
  2956. let listStringName: [String] = ["Yes".localized(), "No".localized()]
  2957. var data : [CategoryCC] = []
  2958. for i in 0..<listStringName.count {
  2959. data.append(CategoryCC(id: "level\(self.dataMessages.count + 1)_\(i)", service_id: service_id, service_name: listStringName[i], parent: "", description: "", is_tablet: "0"))
  2960. }
  2961. row["category_cc"] = data
  2962. self.dataMessages.append(row)
  2963. self.channelContactCenter = "\(channel)"
  2964. self.tableChatView.insertRows(at: [IndexPath(row: self.dataMessages.count - 1, section: 0)], with: .none)
  2965. self.tableChatView.scrollToBottom()
  2966. }
  2967. }
  2968. }
  2969. }
  2970. }
  2971. private func sendReadMessageStatus(chat_id: String, f_pin: String, message_scope_id: String, message_id: String) {
  2972. let message = CoreMessage_TMessageBank.getUpdateRead(p_chat_id: chat_id, p_f_pin: f_pin, p_scope_id: message_scope_id, qty: 1)
  2973. let fPin = message.getBody(key: CoreMessage_TMessageKey.F_PIN)
  2974. let scope = message.getBody(key: CoreMessage_TMessageKey.SCOPE_ID)
  2975. message.mBodies[CoreMessage_TMessageKey.SERVER_DATE] = String(Date().currentTimeMillis())
  2976. if (fPin.elementsEqual("-999") || scope.elementsEqual("16") || scope.elementsEqual("15")){
  2977. return
  2978. }
  2979. DispatchQueue.global().async {
  2980. if let listGroupImages = self.groupImages.first(where: { $0.key == message_id }) {
  2981. let valueListGroupImages = listGroupImages.value
  2982. for i in 0..<valueListGroupImages.count {
  2983. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  2984. do {
  2985. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  2986. "status" : "4"
  2987. ], _where: "message_id = '\(valueListGroupImages[i].messageId)'")
  2988. } catch {
  2989. rollback.pointee = true
  2990. print("Access database error: \(error.localizedDescription)")
  2991. }
  2992. })
  2993. message.mStatus = CoreMessage_TMessageUtil.getTID()
  2994. message.mBodies[CoreMessage_TMessageKey.L_PIN] = f_pin
  2995. message.mBodies[CoreMessage_TMessageKey.MESSAGE_ID] = "-2,\(valueListGroupImages[i].messageId)"
  2996. }
  2997. } else {
  2998. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  2999. do {
  3000. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  3001. "status" : "4"
  3002. ], _where: "message_id = '\(message_id)'")
  3003. } catch {
  3004. rollback.pointee = true
  3005. print("Access database error: \(error.localizedDescription)")
  3006. }
  3007. })
  3008. message.mStatus = CoreMessage_TMessageUtil.getTID()
  3009. message.mBodies[CoreMessage_TMessageKey.L_PIN] = f_pin
  3010. message.mBodies[CoreMessage_TMessageKey.MESSAGE_ID] = "-2,\(message_id)"
  3011. }
  3012. _ = Nexilis.write(message: message)
  3013. }
  3014. if let index = dataMessages.firstIndex(where: {$0["message_id"] as? String == message_id}) {
  3015. dataMessages[index]["status"] = "4"
  3016. let auto: Bool = SecureUserDefaults.shared.value(forKey: "autoDownload") ?? false
  3017. if auto {
  3018. if dataMessages[index]["image_id"] as? String != nil && !((dataMessages[index]["image_id"] as? String)!.isEmpty) {
  3019. if let listGroupImages = self.groupImages.first(where: { $0.key == message_id }) {
  3020. let valueListGroupImages = listGroupImages.value
  3021. for i in 0..<valueListGroupImages.count {
  3022. Download().startHTTP(forKey:valueListGroupImages[i].imageId) { (name, progress) in
  3023. guard progress == 100 else {
  3024. return
  3025. }
  3026. do {
  3027. let secureName = try FileEncryption.shared.writeSecure(filename: valueListGroupImages[i].imageId)?[0]
  3028. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  3029. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  3030. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  3031. if let dirPath = paths.first {
  3032. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(valueListGroupImages[i].imageId)
  3033. if FileManager.default.fileExists(atPath: imageURL.path) {
  3034. let image = UIImage(contentsOfFile: imageURL.path)
  3035. let save: Bool = SecureUserDefaults.shared.value(forKey: "saveToGallery") ?? false
  3036. if save {
  3037. UIImageWriteToSavedPhotosAlbum(image!, nil, nil, nil)
  3038. }
  3039. }
  3040. else if FileEncryption.shared.isSecureExists(filename: valueListGroupImages[i].imageId) {
  3041. if let secureData = try FileEncryption.shared.readSecure(filename: valueListGroupImages[i].imageId) {
  3042. let image = UIImage(data: secureData)
  3043. let save: Bool = SecureUserDefaults.shared.value(forKey: "saveToGallery") ?? false
  3044. if save {
  3045. UIImageWriteToSavedPhotosAlbum(image!, nil, nil, nil)
  3046. }
  3047. }
  3048. }
  3049. }
  3050. }
  3051. catch {
  3052. }
  3053. DispatchQueue.main.async { [self] in
  3054. let section = dataDates.firstIndex(of: dataMessages[index]["chat_date"] as? String ?? "")
  3055. let row = dataMessages.filter({$0["chat_date"] as? String ?? "" == dataMessages[index]["chat_date"] as? String ?? ""}).firstIndex(where: { $0["message_id"] as? String == message_id})
  3056. if row != nil && section != nil{
  3057. tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
  3058. }
  3059. }
  3060. }
  3061. }
  3062. } else {
  3063. Download().startHTTP(forKey:dataMessages[index]["image_id"] as? String ?? "") { (name, progress) in
  3064. guard progress == 100 else {
  3065. return
  3066. }
  3067. do {
  3068. let secureName = try FileEncryption.shared.writeSecure(filename: self.dataMessages[index]["image_id"] as? String)?[0] as? String ?? ""
  3069. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  3070. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  3071. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  3072. if let dirPath = paths.first {
  3073. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(self.dataMessages[index]["image_id"] as? String ?? "")
  3074. if FileManager.default.fileExists(atPath: imageURL.path) {
  3075. let image = UIImage(contentsOfFile: imageURL.path)
  3076. let save: Bool = SecureUserDefaults.shared.value(forKey: "saveToGallery") ?? false
  3077. if save {
  3078. UIImageWriteToSavedPhotosAlbum(image!, nil, nil, nil)
  3079. }
  3080. }
  3081. else if FileEncryption.shared.isSecureExists(filename: secureName) {
  3082. if let secureData = try FileEncryption.shared.readSecure(filename: secureName) {
  3083. let image = UIImage(data: secureData)
  3084. let save: Bool = SecureUserDefaults.shared.value(forKey: "saveToGallery") ?? false
  3085. if save {
  3086. UIImageWriteToSavedPhotosAlbum(image!, nil, nil, nil)
  3087. }
  3088. }
  3089. }
  3090. }
  3091. } catch {
  3092. }
  3093. DispatchQueue.main.async { [self] in
  3094. let section = dataDates.firstIndex(of: dataMessages[index]["chat_date"] as? String ?? "")
  3095. let row = dataMessages.filter({$0["chat_date"] as? String ?? "" == dataMessages[index]["chat_date"] as? String ?? ""}).firstIndex(where: { $0["message_id"] as? String == message_id})
  3096. if row != nil && section != nil{
  3097. tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
  3098. }
  3099. }
  3100. }
  3101. }
  3102. } else if dataMessages[index]["video_id"] as? String != nil && !((dataMessages[index]["video_id"] as? String)!.isEmpty){
  3103. Download().startHTTP(forKey: dataMessages[index]["video_id"] as? String ?? "", isImage: false) { (name, progress) in
  3104. guard progress == 100 else {
  3105. return
  3106. }
  3107. do {
  3108. let secureName = try FileEncryption.shared.writeSecure(filename: self.dataMessages[index]["video_id"] as? String)?[0] as? String ?? ""
  3109. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  3110. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  3111. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  3112. if let dirPath = paths.first {
  3113. let videoURL = URL(fileURLWithPath: dirPath).appendingPathComponent(self.dataMessages[index]["video_id"] as? String ?? "")
  3114. if FileManager.default.fileExists(atPath: videoURL.path) {
  3115. let save: Bool = SecureUserDefaults.shared.value(forKey: "saveToGallery") ?? false
  3116. if save {
  3117. PHPhotoLibrary.shared().performChanges({
  3118. PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: videoURL)
  3119. }) { saved, error in
  3120. }
  3121. }
  3122. }
  3123. else if FileEncryption.shared.isSecureExists(filename: secureName) {
  3124. if let secureData = try FileEncryption.shared.readSecure(filename: secureName) {
  3125. let cachesDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
  3126. let tempPath = cachesDirectory.appendingPathComponent(name)
  3127. try secureData.write(to: tempPath)
  3128. let save: Bool = SecureUserDefaults.shared.value(forKey: "saveToGallery") ?? false
  3129. if save {
  3130. PHPhotoLibrary.shared().performChanges({
  3131. PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: tempPath)
  3132. }) { saved, error in
  3133. }
  3134. }
  3135. }
  3136. }
  3137. }
  3138. } catch {
  3139. }
  3140. DispatchQueue.main.async { [self] in
  3141. let section = dataDates.firstIndex(of: dataMessages[index]["chat_date"] as? String ?? "")
  3142. let row = dataMessages.filter({$0["chat_date"] as? String ?? "" == dataMessages[index]["chat_date"] as? String ?? ""}).firstIndex(where: { $0["message_id"] as? String == message_id})
  3143. if row != nil && section != nil{
  3144. tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
  3145. }
  3146. }
  3147. }
  3148. }
  3149. else if dataMessages[index]["file_id"] as? String != nil && !((dataMessages[index]["file_id"] as? String)!.isEmpty) {
  3150. Download().startHTTP(forKey: dataMessages[index]["file_id"] as? String ?? "", isImage: false) { (name, progress) in
  3151. guard progress == 100 else {
  3152. return
  3153. }
  3154. do {
  3155. try FileEncryption.shared.writeSecure(filename: name)
  3156. } catch {
  3157. }
  3158. DispatchQueue.main.async { [self] in
  3159. let section = dataDates.firstIndex(of: dataMessages[index]["chat_date"] as? String ?? "")
  3160. let row = dataMessages.filter({$0["chat_date"] as? String ?? "" == dataMessages[index]["chat_date"] as? String ?? ""}).firstIndex(where: { $0["message_id"] as? String == message_id})
  3161. if row != nil && section != nil{
  3162. tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
  3163. }
  3164. }
  3165. }
  3166. }
  3167. }
  3168. }
  3169. }
  3170. private func sendTyping(l_pin: String, isTyping: Bool = false) {
  3171. DispatchQueue.global().async {
  3172. let tmessage = CoreMessage_TMessageBank.getUpdateTypingStatus(p_opposite: l_pin, p_scope: "3", p_status: isTyping ? "3": "4")
  3173. _ = Nexilis.write(message: tmessage)
  3174. }
  3175. }
  3176. private func getCounter() {
  3177. Database().database?.inTransaction({ fmdb, rollback in
  3178. if let c = Database().getRecords(fmdb: fmdb, query: "SELECT counter FROM MESSAGE_SUMMARY where l_pin='\(dataPerson["f_pin"]!!)'"), c.next() {
  3179. counter = Int(c.int(forColumnIndex: 0))
  3180. c.close()
  3181. }
  3182. })
  3183. }
  3184. private func updateCounter(counter: Int) {
  3185. DispatchQueue.global().async {
  3186. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  3187. do {
  3188. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE_SUMMARY", cvalues: [
  3189. "counter" : "\(counter)"
  3190. ], _where: "l_pin = '\(self.dataPerson["f_pin"]!!)'")
  3191. } catch {
  3192. rollback.pointee = true
  3193. print("Access database error: \(error.localizedDescription)")
  3194. }
  3195. })
  3196. NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
  3197. }
  3198. }
  3199. private func addButtonScrollToBottom() {
  3200. self.view.addSubview(buttonScrollToBottom)
  3201. buttonScrollToBottom.translatesAutoresizingMaskIntoConstraints = false
  3202. NSLayoutConstraint.activate([
  3203. buttonScrollToBottom.bottomAnchor.constraint(equalTo: buttonSendChat.topAnchor, constant: -50),
  3204. buttonScrollToBottom.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
  3205. buttonScrollToBottom.widthAnchor.constraint(equalToConstant: 60),
  3206. buttonScrollToBottom.heightAnchor.constraint(equalToConstant: 30.0)
  3207. ])
  3208. buttonScrollToBottom.backgroundColor = .greenColor
  3209. buttonScrollToBottom.setImage(UIImage(systemName: "chevron.down.circle"), for: .normal)
  3210. buttonScrollToBottom.imageView?.contentMode = .scaleAspectFit
  3211. buttonScrollToBottom.imageView?.tintColor = .white
  3212. buttonScrollToBottom.contentVerticalAlignment = .fill
  3213. buttonScrollToBottom.contentHorizontalAlignment = .fill
  3214. buttonScrollToBottom.imageEdgeInsets.top = 2.0
  3215. buttonScrollToBottom.imageEdgeInsets.bottom = 2.0
  3216. buttonScrollToBottom.layer.cornerRadius = 10.0
  3217. buttonScrollToBottom.layer.maskedCorners = [.layerMinXMinYCorner, .layerMinXMaxYCorner]
  3218. buttonScrollToBottom.clipsToBounds = true
  3219. buttonScrollToBottom.addTarget(self, action: #selector(scrollTobottomAction), for: .touchUpInside)
  3220. }
  3221. private func addCounterAtButttonScrollToBottom() {
  3222. self.view.addSubview(indicatorCounterBSTB)
  3223. indicatorCounterBSTB.translatesAutoresizingMaskIntoConstraints = false
  3224. indicatorCounterBSTB.backgroundColor = .systemRed
  3225. indicatorCounterBSTB.layer.cornerRadius = 7.5
  3226. indicatorCounterBSTB.clipsToBounds = true
  3227. indicatorCounterBSTB.layer.borderWidth = 0.5
  3228. indicatorCounterBSTB.layer.borderColor = UIColor.secondaryColor.cgColor
  3229. NSLayoutConstraint.activate([
  3230. indicatorCounterBSTB.bottomAnchor.constraint(equalTo: buttonScrollToBottom.topAnchor, constant: 5),
  3231. indicatorCounterBSTB.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -50),
  3232. indicatorCounterBSTB.widthAnchor.constraint(greaterThanOrEqualToConstant: 15),
  3233. indicatorCounterBSTB.heightAnchor.constraint(equalToConstant: 15)
  3234. ])
  3235. indicatorCounterBSTB.addSubview(labelCounter)
  3236. labelCounter.translatesAutoresizingMaskIntoConstraints = false
  3237. NSLayoutConstraint.activate([
  3238. labelCounter.leadingAnchor.constraint(equalTo: indicatorCounterBSTB.leadingAnchor, constant: 2),
  3239. labelCounter.trailingAnchor.constraint(equalTo: indicatorCounterBSTB.trailingAnchor, constant: -2),
  3240. labelCounter.centerXAnchor.constraint(equalTo: indicatorCounterBSTB.centerXAnchor),
  3241. ])
  3242. labelCounter.font = UIFont.systemFont(ofSize: 11)
  3243. labelCounter.text = "\(counter)"
  3244. labelCounter.textColor = .secondaryColor
  3245. labelCounter.textAlignment = .center
  3246. }
  3247. @objc func scrollTobottomAction() {
  3248. tableChatView.scrollToBottom()
  3249. DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) { [self] in
  3250. if buttonScrollToBottom.isDescendant(of: self.view) {
  3251. buttonScrollToBottom.removeConstraints(buttonScrollToBottom.constraints)
  3252. buttonScrollToBottom.removeFromSuperview()
  3253. if indicatorCounterBSTB.isDescendant(of: self.view) {
  3254. indicatorCounterBSTB.removeConstraints(indicatorCounterBSTB.constraints)
  3255. indicatorCounterBSTB.removeFromSuperview()
  3256. }
  3257. }
  3258. }
  3259. }
  3260. private func checkNewMessage(tableView: UITableView) {
  3261. // let indexPathFirst = tableView.indexPathsForVisibleRows?.first
  3262. // if indexPathFirst != nil {
  3263. // let dataMessages = self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == dataDates[indexPathFirst!.section] })
  3264. // if self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[indexPathFirst!.row]["message_id"] as? String }) == 0 && !gettingDataMessage {
  3265. // gettingDataMessage = true
  3266. // addDataMessage()
  3267. // }
  3268. // }
  3269. currentIndexpath = tableView.indexPathsForVisibleRows?.last
  3270. let indexFirst = tableView.indexPathsForVisibleRows?.first
  3271. if indexFirst != nil {
  3272. let dataMessages = dataMessages.filter({ $0["chat_date"] as? String ?? "" == dataDates[currentIndexpath!.section] })
  3273. if dataMessages.count == 0 || dataMessages.count - 1 < currentIndexpath!.row {
  3274. return
  3275. }
  3276. let contentHeight = tableView.contentSize.height
  3277. let scrollViewHeight = tableView.frame.height
  3278. let fullContentOffset = contentHeight - scrollViewHeight
  3279. let contentOffsetY = tableView.contentOffset.y
  3280. if ((currentIndexpath!.section == dataDates.count - 1 && indexFirst!.row != dataMessages.count - 1) || indexFirst!.section != dataDates.count - 1) && fullContentOffset - contentOffsetY > 100 {
  3281. if !buttonScrollToBottom.isDescendant(of: self.view) {
  3282. addButtonScrollToBottom()
  3283. addCounterAtButttonScrollToBottom()
  3284. }
  3285. } else if (indexFirst!.section == dataDates.count - 1 && indexFirst!.row == dataMessages.count - 1) || fullContentOffset - contentOffsetY < 50 {
  3286. if buttonScrollToBottom.isDescendant(of: self.view) {
  3287. buttonScrollToBottom.removeConstraints(buttonScrollToBottom.constraints)
  3288. buttonScrollToBottom.removeFromSuperview()
  3289. if indicatorCounterBSTB.isDescendant(of: self.view) {
  3290. indicatorCounterBSTB.removeConstraints(indicatorCounterBSTB.constraints)
  3291. indicatorCounterBSTB.removeFromSuperview()
  3292. }
  3293. }
  3294. }
  3295. let indexPathFirst = tableChatView.indexPathsForVisibleRows?.first
  3296. if indexPathFirst != nil && listViewOnSection.count != 0 && listViewOnSection.count - 1 >= indexPathFirst!.section {
  3297. let headerView = listViewOnSection[indexPathFirst!.section]
  3298. if headerView.isHidden {
  3299. headerView.isHidden = false
  3300. }
  3301. }
  3302. var listData = dataMessages[0...currentIndexpath!.row]
  3303. listData = listData.filter({$0["status"] as? String != "4" && $0["status"] as? String != "8"})
  3304. if listData.count != 0 && !isContactCenter {
  3305. let idMe = User.getMyPin() as String?
  3306. for i in 0...listData.count - 1 {
  3307. if listData[i]["f_pin"] as? String != idMe {
  3308. sendReadMessageStatus(chat_id: "", f_pin: dataPerson["f_pin"]!!, message_scope_id: "3", message_id: listData[i]["message_id"] as? String ?? "")
  3309. }
  3310. }
  3311. }
  3312. }
  3313. if counter == 0 && indicatorCounterBSTB.isDescendant(of: self.view) {
  3314. indicatorCounterBSTB.removeConstraints(indicatorCounterBSTB.constraints)
  3315. indicatorCounterBSTB.removeFromSuperview()
  3316. } else if counter != 0 && currentIndexpath != nil {
  3317. let dataFilter = dataMessages.filter({ $0["chat_date"] as? String ?? "" == dataDates[currentIndexpath!.section] })
  3318. if dataFilter.count == 0 {
  3319. return
  3320. }
  3321. let idx = dataMessages.firstIndex(where: { $0["message_id"] as? String == dataFilter[currentIndexpath!.row]["message_id"] as? String})
  3322. if idx == nil {
  3323. return
  3324. }
  3325. if (dataMessages.count - counter) <= idx! {
  3326. let countUpdate = idx! - (dataMessages.count - counter)
  3327. counter = counter - (countUpdate + 1)
  3328. if indicatorCounterBSTB.isDescendant(of: self.view) {
  3329. labelCounter.text = "\(counter)"
  3330. }
  3331. updateCounter(counter: counter)
  3332. }
  3333. }
  3334. }
  3335. }
  3336. //EPV
  3337. extension EditorPersonal: PreviewAttachmentImageVideoDelegate, PHPickerViewControllerDelegate {
  3338. public func didSelect(imagevideo: Any?) {
  3339. if (imagevideo != nil) {
  3340. let imageVideoData = imagevideo as! [UIImagePickerController.InfoKey: Any]
  3341. let previewImageVC = PreviewAttachmentImageVideo(nibName: "PreviewAttachmentImageVideo", bundle: Bundle.resourceBundle(for: Nexilis.self))
  3342. previewImageVC.imageVideoData = imageVideoData
  3343. if (textFieldSend.textColor != .lightGray) {
  3344. previewImageVC.currentTextTextField = textFieldSend.text
  3345. }
  3346. previewImageVC.modalPresentationStyle = .custom
  3347. previewImageVC.delegate = self
  3348. previewImageVC.isAck = self.isAck
  3349. previewImageVC.isConfidential = self.isConfidential
  3350. previewImageVC.isCC = self.isContactCenter
  3351. self.present(previewImageVC, animated: true, completion: nil)
  3352. }
  3353. }
  3354. public func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
  3355. if !isBlackCancelButton {
  3356. let cancelButtonAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.font : UIFont.systemFont(ofSize: 16)]
  3357. UIBarButtonItem.appearance().setTitleTextAttributes(cancelButtonAttributes , for: .normal)
  3358. }
  3359. guard let result = results.first else { return }
  3360. if result.itemProvider.hasItemConformingToTypeIdentifier("com.compuserve.gif") {
  3361. picker.dismiss(animated: true, completion: nil)
  3362. result.itemProvider.loadDataRepresentation(forTypeIdentifier: "com.compuserve.gif") { data, error in
  3363. if let error = error {
  3364. print("Error loading GIF: \(error.localizedDescription)")
  3365. } else if let data = data {
  3366. DispatchQueue.main.async {
  3367. let previewImageVC = PreviewAttachmentImageVideo(nibName: "PreviewAttachmentImageVideo", bundle: Bundle.resourceBundle(for: Nexilis.self))
  3368. if (self.textFieldSend.textColor != .lightGray) {
  3369. previewImageVC.currentTextTextField = self.textFieldSend.text
  3370. }
  3371. previewImageVC.fromCopy = true
  3372. previewImageVC.isGIF = true
  3373. previewImageVC.dataGIF = data
  3374. previewImageVC.modalPresentationStyle = .custom
  3375. previewImageVC.delegate = self
  3376. previewImageVC.isAck = self.isAck
  3377. previewImageVC.isConfidential = self.isConfidential
  3378. previewImageVC.isCC = self.isContactCenter
  3379. self.present(previewImageVC, animated: true, completion: nil)
  3380. }
  3381. }
  3382. }
  3383. } else if result.itemProvider.hasItemConformingToTypeIdentifier("public.image") {
  3384. picker.dismiss(animated: true, completion: nil)
  3385. result.itemProvider.loadObject(ofClass: UIImage.self) { object, error in
  3386. if let image = object as? UIImage {
  3387. DispatchQueue.main.async {
  3388. let previewImageVC = PreviewAttachmentImageVideo(nibName: "PreviewAttachmentImageVideo", bundle: Bundle.resourceBundle(for: Nexilis.self))
  3389. if (self.textFieldSend.textColor != .lightGray) {
  3390. previewImageVC.currentTextTextField = self.textFieldSend.text
  3391. }
  3392. previewImageVC.fromCopy = true
  3393. previewImageVC.image = image
  3394. previewImageVC.modalPresentationStyle = .custom
  3395. previewImageVC.delegate = self
  3396. previewImageVC.isAck = self.isAck
  3397. previewImageVC.isConfidential = self.isConfidential
  3398. previewImageVC.isCC = self.isContactCenter
  3399. self.present(previewImageVC, animated: true, completion: nil)
  3400. }
  3401. }
  3402. }
  3403. } else if result.itemProvider.hasItemConformingToTypeIdentifier("public.movie") {
  3404. picker.dismiss(animated: true, completion: {
  3405. Nexilis.showLoader()
  3406. result.itemProvider.loadFileRepresentation(forTypeIdentifier: "public.movie") { tempURL, error in
  3407. if let tempURL = tempURL {
  3408. let fileManager = FileManager.default
  3409. let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
  3410. let destinationURL = documentsDirectory.appendingPathComponent(tempURL.lastPathComponent)
  3411. do {
  3412. if fileManager.fileExists(atPath: destinationURL.path) {
  3413. try fileManager.removeItem(at: destinationURL)
  3414. }
  3415. try fileManager.copyItem(at: tempURL, to: destinationURL)
  3416. DispatchQueue.main.async {
  3417. Nexilis.hideLoader {
  3418. let previewImageVC = PreviewAttachmentImageVideo(nibName: "PreviewAttachmentImageVideo", bundle: Bundle.resourceBundle(for: Nexilis.self))
  3419. if (self.textFieldSend.textColor != .lightGray) {
  3420. previewImageVC.currentTextTextField = self.textFieldSend.text
  3421. }
  3422. previewImageVC.modalPresentationStyle = .custom
  3423. previewImageVC.urlVideoPhpPicker = destinationURL
  3424. previewImageVC.delegate = self
  3425. previewImageVC.isAck = self.isAck
  3426. previewImageVC.isConfidential = self.isConfidential
  3427. previewImageVC.isCC = self.isContactCenter
  3428. self.present(previewImageVC, animated: true, completion: nil)
  3429. }
  3430. }
  3431. } catch {
  3432. print("Error copying video file: \(error.localizedDescription)")
  3433. }
  3434. }
  3435. }
  3436. })
  3437. }
  3438. }
  3439. func sendChatFromPreviewImage(message_text: String, attachment_flag: String, image_id: String, video_id: String, thumb_id: String, gif_id: String, viewController: UIViewController) {
  3440. sendChat(message_text: message_text, attachment_flag: attachment_flag, image_id: image_id, video_id: video_id, thumb_id: thumb_id, viewController: viewController, gif_id : gif_id)
  3441. }
  3442. }
  3443. //EQL
  3444. extension EditorPersonal: UIDocumentPickerDelegate, DocumentPickerDelegate, QLPreviewControllerDataSource {
  3445. public func didSelectDocument(document: Any?) {
  3446. if (document != nil) {
  3447. self.previewItem = (document as! [URL])[0] as NSURL
  3448. let previewController = QLPreviewController()
  3449. let navController = CustomNavigationController(rootViewController: previewController)
  3450. navController.navigationBar.tintColor = .white
  3451. navController.navigationBar.barTintColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : .mainColor
  3452. navController.navigationBar.isTranslucent = false
  3453. navController.navigationBar.overrideUserInterfaceStyle = .dark
  3454. navController.navigationBar.barStyle = .black
  3455. let cancelButtonAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.font : UIFont.systemFont(ofSize: 16)]
  3456. UIBarButtonItem.appearance().setTitleTextAttributes(cancelButtonAttributes, for: .normal)
  3457. let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
  3458. navController.navigationBar.titleTextAttributes = textAttributes
  3459. let leftBarButton = navigationQLPreviewDocument(title: "Cancel".localized(), style: .plain, target: self, action: #selector(cancelDocumentPreview))
  3460. let rightBarButton = navigationQLPreviewDocument(title: "Send".localized(), style: .done, target: self, action: #selector(sendDocument))
  3461. // leftBarButton.tintColor = .white
  3462. // rightBarButton.tintColor = .white
  3463. leftBarButton.navigation = navController
  3464. rightBarButton.navigation = navController
  3465. // navController.navigationBar.barTintColor = .mainColor
  3466. navController.navigationBar.isTranslucent = false
  3467. previewController.navigationItem.leftBarButtonItem = leftBarButton
  3468. previewController.navigationItem.rightBarButtonItem = rightBarButton
  3469. previewController.dataSource = self
  3470. previewController.modalPresentationStyle = .pageSheet
  3471. self.present(navController, animated: true, completion: nil)
  3472. }
  3473. }
  3474. @objc private func cancelDocumentPreview(sender: navigationQLPreviewDocument) {
  3475. sender.navigation.dismiss(animated: true, completion: nil)
  3476. }
  3477. @objc private func sendDocument(sender: navigationQLPreviewDocument) {
  3478. sender.navigation.dismiss(animated: true, completion: nil)
  3479. do {
  3480. let dataFile = try Data(contentsOf: self.previewItem! as URL)
  3481. let urlFile = self.previewItem?.absoluteString
  3482. var originaFileName = (urlFile! as NSString).lastPathComponent
  3483. originaFileName = NSString(string: originaFileName).removingPercentEncoding!
  3484. let renamedNameFile = "Nexilis_\(Date().currentTimeMillis())_" + originaFileName
  3485. let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
  3486. let fileURL = documentsDirectory.appendingPathComponent(renamedNameFile)
  3487. if !FileManager.default.fileExists(atPath: fileURL.path) {
  3488. do {
  3489. try dataFile.write(to: fileURL)
  3490. //print("file saved")
  3491. } catch {
  3492. //print("error saving file:", error)
  3493. }
  3494. }
  3495. sendChat(message_text: "\(originaFileName)|", attachment_flag: "6", file_id: renamedNameFile, viewController: self)
  3496. } catch {
  3497. }
  3498. }
  3499. public func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
  3500. return self.previewItem != nil ? 1 : 0
  3501. }
  3502. public func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
  3503. return self.previewItem!
  3504. }
  3505. }
  3506. //ETV
  3507. extension EditorPersonal: UITextViewDelegate {
  3508. public func textViewDidChangeSelection(_ textView: UITextView) {
  3509. var nowTextFieldSend = self.textFieldSend
  3510. if isEditingMessage {
  3511. nowTextFieldSend = editTextView
  3512. }
  3513. let cursorPosition = textView.caretRect(for: nowTextFieldSend!.selectedTextRange!.start).origin
  3514. let doubleCurrentLine = cursorPosition.y / nowTextFieldSend!.font!.lineHeight
  3515. if doubleCurrentLine.isFinite {
  3516. let currentLine = Int(doubleCurrentLine)
  3517. UIView.animate(withDuration: 0.3) {
  3518. let numberOfLines = textView.textContainer.lineBreakMode == .byWordWrapping ? Int(textView.contentSize.height / textView.font!.lineHeight) - 1 : 1
  3519. if currentLine == 0 && numberOfLines == 1 {
  3520. if self.isEditingMessage {
  3521. self.constraintHeighteditTextView.constant = 40
  3522. } else {
  3523. self.heightTextFieldSend.constant = 40
  3524. }
  3525. } else if (self.heightTextFieldSend.constant < 95.0 || (self.constraintHeighteditTextView != nil && self.constraintHeighteditTextView.constant < 95.0)) && currentLine >= 4 {
  3526. if self.isEditingMessage {
  3527. self.constraintHeighteditTextView.constant = 95.0
  3528. } else {
  3529. self.heightTextFieldSend.constant = 95.0
  3530. }
  3531. } else if currentLine < 4 && numberOfLines < 5 {
  3532. if (nowTextFieldSend!.text.count > 0 && self.heightTextFieldSend.constant != nowTextFieldSend!.contentSize.height) {
  3533. if self.isEditingMessage {
  3534. self.constraintHeighteditTextView.constant = nowTextFieldSend!.contentSize.height
  3535. } else {
  3536. self.heightTextFieldSend.constant = nowTextFieldSend!.contentSize.height
  3537. }
  3538. }
  3539. }
  3540. }
  3541. }
  3542. }
  3543. public func textViewDidChange(_ textView: UITextView) {
  3544. if textView.text.count == 0 {
  3545. isAlwaysHideLinkPreview = false
  3546. }
  3547. if allowTyping {
  3548. allowTyping = false
  3549. if isContactCenter && !fPinContacCenter.isEmpty {
  3550. sendTyping(l_pin: fPinContacCenter, isTyping: true)
  3551. } else {
  3552. sendTyping(l_pin: dataPerson["f_pin"]!!, isTyping: true)
  3553. }
  3554. DispatchQueue.main.asyncAfter(deadline: .now() + 4, execute: {
  3555. self.allowTyping = true
  3556. })
  3557. }
  3558. timerCheckLink?.invalidate()
  3559. timerCheckLink = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: {_ in
  3560. self.checkLink(fullText: textView.text)
  3561. })
  3562. textView.preserveCursorPosition(withChanges: { _ in
  3563. textView.attributedText = textView.text.richText(isEditing: true)
  3564. return .preserveCursor
  3565. })
  3566. }
  3567. private func checkLink(fullText: String) {
  3568. if !isAlwaysHideLinkPreview {
  3569. var text = ""
  3570. let listTextSplitBreak = fullText.components(separatedBy: "\n")
  3571. let indexFirstLinkSplitBreak = listTextSplitBreak.firstIndex(where: { $0.contains("www.") || $0.contains("http://") || $0.contains("https://") })
  3572. if indexFirstLinkSplitBreak != nil {
  3573. let listTextSplitSpace = listTextSplitBreak[indexFirstLinkSplitBreak!].components(separatedBy: " ")
  3574. let indexFirstLinkSplitSpace = listTextSplitSpace.firstIndex(where: { ($0.starts(with: "www.") && $0.components(separatedBy: ".").count > 2) || ($0.starts(with: "http://") && $0.components(separatedBy: ".").count > 1) || ($0.starts(with: "https://") && $0.components(separatedBy: ".").count > 1) })
  3575. if indexFirstLinkSplitSpace != nil {
  3576. text = listTextSplitSpace[indexFirstLinkSplitSpace!]
  3577. }
  3578. }
  3579. if !text.isEmpty {
  3580. var stringURl = text
  3581. if stringURl.starts(with: "www.") {
  3582. stringURl = "https://" + stringURl.replacingOccurrences(of: "www.", with: "")
  3583. }
  3584. var dataURL = ""
  3585. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  3586. do {
  3587. if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "select data_link from LINK_PREVIEW where link='\(text)'") {
  3588. while cursor.next() {
  3589. if let data = cursor.string(forColumnIndex: 0) {
  3590. dataURL = data
  3591. }
  3592. }
  3593. cursor.close()
  3594. }
  3595. } catch {
  3596. rollback.pointee = true
  3597. print("Access database error: \(error.localizedDescription)")
  3598. }
  3599. })
  3600. if !dataURL.isEmpty {
  3601. if let data = try! JSONSerialization.jsonObject(with: dataURL.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
  3602. let title = data["title"] as? String ?? ""
  3603. let description = data["description"] as? String ?? ""
  3604. let imageUrl = data["imageUrl"] as? String
  3605. let link = data["link"] as? String ?? ""
  3606. if self.showingLink != text {
  3607. self.showingLink = text
  3608. self.deleteLinkPreview()
  3609. self.buildPreviewLink(imageUrl: imageUrl, title: title, description: description, stringURl: link)
  3610. }
  3611. }
  3612. } else {
  3613. let urlConfig = URLSessionConfiguration.default
  3614. let sessionDelegate = SelfSignedURLSessionDelegate()
  3615. let session = URLSession(configuration: urlConfig, delegate: sessionDelegate, delegateQueue: nil)
  3616. let slp = SwiftLinkPreview(session: session,
  3617. workQueue: SwiftLinkPreview.defaultWorkQueue,
  3618. responseQueue: DispatchQueue.main,
  3619. cache: DisabledCache.instance)
  3620. let preview = slp.preview(stringURl,
  3621. onSuccess: { result in
  3622. let title = result.title ?? "No Title"
  3623. let description = stringURl.contains("google.com") ? "" : result.description
  3624. let imageUrl = result.icon
  3625. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  3626. do {
  3627. var dataJson: [String: Any] = [:]
  3628. dataJson["title"] = title
  3629. dataJson["description"] = description
  3630. dataJson["imageUrl"] = imageUrl
  3631. dataJson["link"] = text
  3632. guard let json = String(data: try! JSONSerialization.data(withJSONObject: dataJson, options: []), encoding: String.Encoding.utf8) else {
  3633. return
  3634. }
  3635. _ = try Database.shared.insertRecord(fmdb: fmdb, table: "LINK_PREVIEW", cvalues: [
  3636. "id" : "\(Date().currentTimeMillis().toHex())",
  3637. "link" : text,
  3638. "data_link" : json,
  3639. "retry": 0
  3640. ], replace: true)
  3641. } catch {
  3642. rollback.pointee = true
  3643. print("Access database error: \(error.localizedDescription)")
  3644. }
  3645. })
  3646. if self.showingLink != text {
  3647. self.showingLink = text
  3648. self.deleteLinkPreview()
  3649. self.buildPreviewLink(imageUrl: imageUrl, title: title, description: description, stringURl: text)
  3650. }
  3651. },
  3652. onError: { error in
  3653. self.deleteLinkPreview()
  3654. })
  3655. }
  3656. } else {
  3657. deleteLinkPreview()
  3658. }
  3659. }
  3660. }
  3661. private func buildPreviewLink(imageUrl: String?, title: String, description: String?, stringURl: String) {
  3662. if !self.viewTextfield.subviews.contains(self.containerLink){
  3663. UIView.animate(withDuration: 0.25, delay: 0.0, options: .curveEaseInOut, animations: {
  3664. self.constraintTopTextField.constant = self.constraintTopTextField.constant + 80
  3665. }, completion: nil)
  3666. }
  3667. self.viewTextfield.addSubview(self.containerLink)
  3668. self.containerLink.translatesAutoresizingMaskIntoConstraints = false
  3669. self.containerLink.leadingAnchor.constraint(equalTo: self.viewTextfield.leadingAnchor).isActive = true
  3670. self.containerLink.bottomAnchor.constraint(equalTo: self.textFieldSend.topAnchor).isActive = true
  3671. self.containerLink.trailingAnchor.constraint(equalTo: self.viewTextfield.trailingAnchor).isActive = true
  3672. self.containerLink.heightAnchor.constraint(equalToConstant: 80.0).isActive = true
  3673. self.containerLink.backgroundColor = .secondaryColor
  3674. if self.reffId != nil {
  3675. self.bottomAnchorPreviewReply.isActive = false
  3676. self.bottomAnchorPreviewReply = self.containerPreviewReply.bottomAnchor.constraint(equalTo: self.containerLink.topAnchor)
  3677. self.bottomAnchorPreviewReply.isActive = true
  3678. }
  3679. let imagePreview = UIImageView()
  3680. if imageUrl != nil {
  3681. self.containerLink.addSubview(imagePreview)
  3682. imagePreview.translatesAutoresizingMaskIntoConstraints = false
  3683. imagePreview.leadingAnchor.constraint(equalTo: self.containerLink.leadingAnchor).isActive = true
  3684. imagePreview.bottomAnchor.constraint(equalTo: self.containerLink.bottomAnchor).isActive = true
  3685. imagePreview.topAnchor.constraint(equalTo: self.containerLink.topAnchor).isActive = true
  3686. imagePreview.widthAnchor.constraint(equalToConstant: 80.0).isActive = true
  3687. imagePreview.loadImageAsync(with: imageUrl)
  3688. imagePreview.contentMode = .scaleAspectFit
  3689. }
  3690. let titlePreview = UILabel()
  3691. self.containerLink.addSubview(titlePreview)
  3692. titlePreview.translatesAutoresizingMaskIntoConstraints = false
  3693. if imageUrl != nil {
  3694. titlePreview.leadingAnchor.constraint(equalTo: imagePreview.trailingAnchor, constant: 5.0).isActive = true
  3695. } else {
  3696. titlePreview.leadingAnchor.constraint(equalTo: self.containerLink.leadingAnchor, constant: 5.0).isActive = true
  3697. }
  3698. titlePreview.topAnchor.constraint(equalTo: self.containerLink.topAnchor, constant: 25.0).isActive = true
  3699. titlePreview.trailingAnchor.constraint(equalTo: self.containerLink.trailingAnchor, constant: -80.0).isActive = true
  3700. titlePreview.text = title
  3701. titlePreview.font = UIFont.systemFont(ofSize: 14.0, weight: .bold)
  3702. titlePreview.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
  3703. let descPreview = UILabel()
  3704. self.containerLink.addSubview(descPreview)
  3705. descPreview.translatesAutoresizingMaskIntoConstraints = false
  3706. if imageUrl != nil {
  3707. descPreview.leadingAnchor.constraint(equalTo: imagePreview.trailingAnchor, constant: 5.0).isActive = true
  3708. } else {
  3709. descPreview.leadingAnchor.constraint(equalTo: self.containerLink.leadingAnchor, constant: 5.0).isActive = true
  3710. }
  3711. descPreview.topAnchor.constraint(equalTo: titlePreview.bottomAnchor).isActive = true
  3712. descPreview.trailingAnchor.constraint(equalTo: self.containerLink.trailingAnchor, constant: -80.0).isActive = true
  3713. descPreview.text = description
  3714. descPreview.font = UIFont.systemFont(ofSize: 12.0)
  3715. descPreview.textColor = .gray
  3716. descPreview.numberOfLines = 1
  3717. let linkPreview = UILabel()
  3718. self.containerLink.addSubview(linkPreview)
  3719. linkPreview.translatesAutoresizingMaskIntoConstraints = false
  3720. if imageUrl != nil {
  3721. linkPreview.leadingAnchor.constraint(equalTo: imagePreview.trailingAnchor, constant: 5.0).isActive = true
  3722. } else {
  3723. linkPreview.leadingAnchor.constraint(equalTo: self.containerLink.leadingAnchor, constant: 5.0).isActive = true
  3724. }
  3725. linkPreview.topAnchor.constraint(equalTo: descPreview.bottomAnchor, constant: 8.0).isActive = true
  3726. linkPreview.trailingAnchor.constraint(equalTo: self.containerLink.trailingAnchor, constant: -80.0).isActive = true
  3727. linkPreview.text = stringURl
  3728. linkPreview.font = UIFont.systemFont(ofSize: 10.0)
  3729. linkPreview.textColor = .gray
  3730. linkPreview.numberOfLines = 1
  3731. let cancelPreview = UIButton(type: .custom)
  3732. self.containerLink.addSubview(cancelPreview)
  3733. cancelPreview.translatesAutoresizingMaskIntoConstraints = false
  3734. cancelPreview.trailingAnchor.constraint(equalTo: self.containerLink.trailingAnchor, constant: -10).isActive = true
  3735. cancelPreview.centerYAnchor.constraint(equalTo: self.containerLink.centerYAnchor).isActive = true
  3736. cancelPreview.setImage(UIImage(systemName: "xmark.circle" , withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .regular, scale: .default)), for: .normal)
  3737. cancelPreview.addTarget(nil, action: #selector(self.removeLinkPreviewUntilEmptyTextView), for: .touchUpInside)
  3738. cancelPreview.backgroundColor = .clear
  3739. cancelPreview.tintColor = .mainColor
  3740. }
  3741. public func textViewDidBeginEditing(_ textView: UITextView) {
  3742. if textView.textColor == UIColor.lightGray {
  3743. textView.text = nil
  3744. textView.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : UIColor.black
  3745. }
  3746. }
  3747. public func textViewDidEndEditing(_ textView: UITextView) {
  3748. if textView.text.isEmpty && textView != editTextView {
  3749. textView.text = "Send message".localized()
  3750. textView.textColor = UIColor.lightGray
  3751. }
  3752. }
  3753. public func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
  3754. if let pasteboardItems = UIPasteboard.general.items.first {
  3755. if pasteboardItems["public.jpeg"] != nil || pasteboardItems["public.png"] != nil || pasteboardItems["public.gif"] != nil || (pasteboardItems.keys.first != nil && pasteboardItems.keys.first!.contains(".gif")) {
  3756. let dataGif = UIPasteboard.general.data(forPasteboardType: "com.compuserve.gif")
  3757. let previewImageVC = PreviewAttachmentImageVideo(nibName: "PreviewAttachmentImageVideo", bundle: Bundle.resourceBundle(for: Nexilis.self))
  3758. previewImageVC.image = pasteboardItems["public.png"] as? UIImage ?? pasteboardItems["public.jpeg"] as? UIImage
  3759. previewImageVC.isGIF = (pasteboardItems["public.png"] == nil && pasteboardItems["public.jpeg"] == nil)
  3760. previewImageVC.fromCopy = true
  3761. previewImageVC.dataGIF = dataGif
  3762. previewImageVC.currentTextTextField = textFieldSend.text
  3763. previewImageVC.modalPresentationStyle = .custom
  3764. previewImageVC.delegate = self
  3765. previewImageVC.isAck = self.isAck
  3766. previewImageVC.isConfidential = self.isConfidential
  3767. previewImageVC.isCC = self.isContactCenter
  3768. self.present(previewImageVC, animated: true, completion: nil)
  3769. return false
  3770. }
  3771. }
  3772. if (self.textFieldSend.text.count == 0) {
  3773. return text != "\n"
  3774. }
  3775. return true
  3776. }
  3777. func isGIFData(_ data: Data) -> Bool {
  3778. let gifSignature: [UInt8] = [0x47, 0x49, 0x46, 0x38, 0x37, 0x61]
  3779. let rawData = [UInt8](data.prefix(6))
  3780. return rawData == gifSignature
  3781. }
  3782. public func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
  3783. switch interaction {
  3784. case .invokeDefaultAction:
  3785. let gesture = ObjectGesture()
  3786. gesture.message_id = URL.absoluteString
  3787. tapMessageText(gesture)
  3788. return false
  3789. case .presentActions:
  3790. UIPasteboard.general.string = URL.absoluteString
  3791. self.view.makeToast("Link Copied".localized(), duration: 3)
  3792. return false
  3793. case .preview:
  3794. return true
  3795. @unknown default:
  3796. return true
  3797. }
  3798. }
  3799. }
  3800. //EUC
  3801. extension EditorPersonal: UIContextMenuInteractionDelegate {
  3802. public func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willEndFor configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionAnimating?) {
  3803. if showMenuContext {
  3804. showMenuContext = false
  3805. interaction.view!.removeInteraction(interaction)
  3806. }
  3807. }
  3808. public func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
  3809. if textFieldSend.isFirstResponder {
  3810. textFieldSend.resignFirstResponder()
  3811. }
  3812. let indexPath = self.tableChatView.indexPathForRow(at: interaction.view!.convert(location, to: self.tableChatView))
  3813. let dataMessages = self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == dataDates[indexPath!.section]})
  3814. var star: UIAction
  3815. if (dataMessages[indexPath!.row]["is_stared"] as? String ?? "" == "0") {
  3816. star = UIAction(title: "Star".localized(), image: UIImage(systemName: "star"), handler: {(_) in
  3817. if self.removed {
  3818. return
  3819. }
  3820. DispatchQueue.global().async {
  3821. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  3822. do {
  3823. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  3824. "is_stared" : 1
  3825. ], _where: "message_id = '\(dataMessages[indexPath!.row]["message_id"] as? String ?? "")'")
  3826. } catch {
  3827. rollback.pointee = true
  3828. print("Access database error: \(error.localizedDescription)")
  3829. }
  3830. })
  3831. }
  3832. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[indexPath!.row]["message_id"] as? String})
  3833. if idx != nil{
  3834. self.dataMessages[idx!]["is_stared"] = "1"
  3835. }
  3836. self.tableChatView.reloadRows(at: [indexPath!], with: .none)
  3837. NotificationCenter.default.post(name: NSNotification.Name(rawValue: "listenerStarMessage"), object: nil, userInfo: nil)
  3838. })
  3839. } else {
  3840. star = UIAction(title: "Unstar".localized(), image: UIImage(systemName: "star.slash"), handler: {(_) in
  3841. if self.removed {
  3842. return
  3843. }
  3844. DispatchQueue.global().async {
  3845. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  3846. do {
  3847. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  3848. "is_stared" : 0
  3849. ], _where: "message_id = '\(dataMessages[indexPath!.row]["message_id"] as? String ?? "")'")
  3850. } catch {
  3851. rollback.pointee = true
  3852. print("Access database error: \(error.localizedDescription)")
  3853. }
  3854. })
  3855. }
  3856. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[indexPath!.row]["message_id"] as? String})
  3857. if idx != nil{
  3858. self.dataMessages[idx!]["is_stared"] = "0"
  3859. }
  3860. self.tableChatView.reloadRows(at: [indexPath!], with: .none)
  3861. NotificationCenter.default.post(name: NSNotification.Name(rawValue: "listenerStarMessage"), object: nil, userInfo: nil)
  3862. })
  3863. }
  3864. let reply = UIAction(title: "Reply".localized(), image: UIImage(systemName: "arrowshape.turn.up.left"), handler: {(_) in
  3865. if self.removed {
  3866. return
  3867. }
  3868. if self.isSearching {
  3869. self.cancelAction()
  3870. }
  3871. DispatchQueue.main.asyncAfter(deadline: .now() + 0.35, execute: {
  3872. self.handleReply(indexPath: indexPath!)
  3873. })
  3874. })
  3875. let forward = UIAction(title: "Forward".localized(), image: UIImage(systemName: "arrowshape.turn.up.right"), handler: {(_) in
  3876. if self.removed {
  3877. return
  3878. }
  3879. if self.isSearching {
  3880. self.cancelAction()
  3881. }
  3882. if self.reffId != nil {
  3883. self.deleteReplyView()
  3884. }
  3885. DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
  3886. self.forwardSession = true
  3887. let cancelButton = UIBarButtonItem(title: "Cancel".localized(), style: .plain, target: self, action: #selector(self.cancelAction))
  3888. cancelButton.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16)], for: .normal)
  3889. if self.dataPerson["f_pin"] != "-999" && !self.isContactCenter {
  3890. self.navigationItem.rightBarButtonItems = nil
  3891. }
  3892. self.navigationItem.rightBarButtonItem = cancelButton
  3893. if self.isContactCenter || self.fromNotification {
  3894. self.navigationItem.leftBarButtonItem = nil
  3895. }
  3896. self.changeAppBar()
  3897. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[indexPath!.row]["message_id"] as? String})
  3898. if idx != nil{
  3899. self.dataMessages[idx!]["isSelected"] = true
  3900. }
  3901. self.addMultipleSelectSession()
  3902. self.tableChatView.reloadData()
  3903. }
  3904. })
  3905. let copy = UIAction(title: "Copy".localized(), image: UIImage(systemName: "doc.on.doc"), handler: {(_) in
  3906. if self.removed {
  3907. return
  3908. }
  3909. if self.isSearching {
  3910. self.cancelAction()
  3911. }
  3912. if self.reffId != nil {
  3913. self.deleteReplyView()
  3914. }
  3915. DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
  3916. self.copySession = true
  3917. let cancelButton = UIBarButtonItem(title: "Cancel".localized(), style: .plain, target: self, action: #selector(self.cancelAction))
  3918. cancelButton.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16)], for: .normal)
  3919. if self.dataPerson["f_pin"] != "-999" && !self.isContactCenter {
  3920. self.navigationItem.rightBarButtonItems = nil
  3921. }
  3922. self.navigationItem.rightBarButtonItem = cancelButton
  3923. if self.isContactCenter || self.fromNotification {
  3924. self.navigationItem.leftBarButtonItem = nil
  3925. }
  3926. self.changeAppBar()
  3927. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[indexPath!.row]["message_id"] as? String})
  3928. if idx != nil{
  3929. self.dataMessages[idx!]["isSelected"] = true
  3930. }
  3931. self.addMultipleSelectSession()
  3932. self.tableChatView.reloadData()
  3933. }
  3934. })
  3935. let edit = UIAction(title: "Edit".localized(), image: UIImage(systemName: "pencil.tip.crop.circle"), handler: {(_) in
  3936. self.isEditingMessage = true
  3937. self.showEditMessageView(at: indexPath!)
  3938. })
  3939. let translate = UIAction(title: "Translate".localized(), image: UIImage(systemName: "t.bubble"), handler: {(_) in
  3940. self.view.makeToast("Translating...".localized(), duration: 3)
  3941. var translation: String = "English"
  3942. let lang: String = SecureUserDefaults.shared.value(forKey: "i18n_language") ?? "en"
  3943. if lang == "id" {
  3944. translation = "Indonesia"
  3945. }
  3946. let payload: [String : Any] = [
  3947. "role": "user",
  3948. "content": dataMessages[indexPath!.row][TypeDataMessage.message_text]!!
  3949. ]
  3950. let parameter: [String : Any] = [
  3951. "use_video": "0",
  3952. "translate": translation,
  3953. "payload": [payload]
  3954. ]
  3955. DispatchQueue.global().async {
  3956. Utils.postDataWithCookiesAndUserAgent(from: URL(string: Utils.getGPTBotUrl())!, parameter: parameter, completion: { data, response, error in
  3957. let response = response as? HTTPURLResponse
  3958. if response?.statusCode != 200 || error != nil {
  3959. DispatchQueue.main.async {
  3960. self.view.makeToast("There is an error occurred while translating your message. Please try again or check your network connection.".localized(), duration: 3)
  3961. }
  3962. return
  3963. }
  3964. if let data = data, let responseString = String(data: data, encoding: .utf8) {
  3965. if let json = try? JSONSerialization.jsonObject(with: responseString.data(using: String.Encoding.utf8)!, options: JSONSerialization.ReadingOptions()) as? [String: String] {
  3966. let dataContent = json["content"]!
  3967. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[indexPath!.row]["message_id"] as? String})
  3968. if idx != nil{
  3969. self.dataMessages[idx!][TypeDataMessage.message_text] = (dataMessages[indexPath!.row][TypeDataMessage.message_text] as? String ?? "") + "\n\n" + "$\(dataContent)$"
  3970. }
  3971. DispatchQueue.main.async{
  3972. self.tableChatView.reloadRows(at: [indexPath!], with: .none)
  3973. }
  3974. }
  3975. }
  3976. })
  3977. }
  3978. })
  3979. let gcs = UIAction(title: "Get Chat Suggestion".localized(), image: UIImage(systemName: "exclamationmark.bubble"), handler: {(_) in
  3980. self.view.makeToast("Getting chat suggestion...".localized(), duration: 3)
  3981. let payload: [String : Any] = [
  3982. "role": "user",
  3983. "content": dataMessages[indexPath!.row][TypeDataMessage.message_text]!!
  3984. ]
  3985. let parameter: [String : Any] = [
  3986. "use_video": "0",
  3987. "suggest": "1",
  3988. "payload": [payload]
  3989. ]
  3990. DispatchQueue.global().async {
  3991. Utils.postDataWithCookiesAndUserAgent(from: URL(string: Utils.getGPTBotUrl())!, parameter: parameter, completion: { data, response, error in
  3992. let response = response as? HTTPURLResponse
  3993. if response?.statusCode != 200 || error != nil {
  3994. DispatchQueue.main.async {
  3995. self.view.makeToast("There is an error occurred while getting chat suggestion for you. Please try again or check your network connection.".localized(), duration: 3)
  3996. }
  3997. return
  3998. }
  3999. if let data = data, let responseString = String(data: data, encoding: .utf8) {
  4000. if let json = try? JSONSerialization.jsonObject(with: responseString.data(using: String.Encoding.utf8)!, options: JSONSerialization.ReadingOptions()) as? [String: Any] {
  4001. if let dataMessage = json["message"] as? [[String: Any]] {
  4002. if let dataContent = dataMessage[0]["content"] as? String {
  4003. DispatchQueue.main.async{
  4004. self.textFieldSend.text = dataContent
  4005. self.textFieldSend.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : UIColor.black
  4006. }
  4007. }
  4008. }
  4009. }
  4010. }
  4011. })
  4012. }
  4013. })
  4014. let more = UIMenu(title: "More...".localized(), children: [translate, gcs])
  4015. let info = UIAction(title: "Info".localized(), image: UIImage(systemName: "info.circle"), handler: {(_) in
  4016. if self.removed {
  4017. return
  4018. }
  4019. let messageInfoVC = MessageInfo()
  4020. messageInfoVC.data = dataMessages[indexPath!.row]
  4021. messageInfoVC.dataPerson = self.dataPerson
  4022. self.navigationController?.pushViewController(messageInfoVC, animated: true)
  4023. })
  4024. let delete = UIAction(title: "Delete".localized(), image: UIImage(systemName: "trash"), attributes: .destructive, handler: {(_) in
  4025. if self.removed {
  4026. return
  4027. }
  4028. if self.isSearching {
  4029. self.cancelAction()
  4030. }
  4031. if self.reffId != nil {
  4032. self.deleteReplyView()
  4033. }
  4034. DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
  4035. self.deleteSession = true
  4036. let cancelButton = UIBarButtonItem(title: "Cancel".localized(), style: .plain, target: self, action: #selector(self.cancelAction))
  4037. cancelButton.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16)], for: .normal)
  4038. if self.dataPerson["f_pin"] != "-999" && !self.isContactCenter {
  4039. self.navigationItem.rightBarButtonItems = nil
  4040. }
  4041. self.navigationItem.rightBarButtonItem = cancelButton
  4042. if self.isContactCenter || self.fromNotification {
  4043. self.navigationItem.leftBarButtonItem = nil
  4044. }
  4045. self.changeAppBar()
  4046. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[indexPath!.row]["message_id"] as? String})
  4047. if idx != nil{
  4048. self.dataMessages[idx!]["isSelected"] = true
  4049. }
  4050. self.addMultipleSelectSession()
  4051. self.tableChatView.reloadData()
  4052. }
  4053. })
  4054. let resend = UIAction(title: "Resend".localized(), image: UIImage(systemName: "arrow.clockwise"), handler: {(_) in
  4055. let messageId = dataMessages[indexPath!.row][TypeDataMessage.message_id] as? String ?? ""
  4056. let status = dataMessages[indexPath!.row][TypeDataMessage.status] as? String ?? ""
  4057. var idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String ?? "" == messageId })
  4058. if let idxMessageIdParent = self.groupImages.firstIndex(where: { $0.value.contains(where: { $0.messageId == messageId }) }) {
  4059. if let idxInImages = self.groupImages[idxMessageIdParent].value.firstIndex(where: { $0.messageId == messageId }) {
  4060. self.groupImages[idxMessageIdParent].value[idxInImages].status = "1"
  4061. self.groupImages[idxMessageIdParent].value[idxInImages].dataMessage[TypeDataMessage.status] = "1"
  4062. }
  4063. idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == self.groupImages[idxMessageIdParent].key })
  4064. }
  4065. if (idx != nil) {
  4066. do {
  4067. self.dataMessages[idx!][TypeDataMessage.status] = "1"
  4068. self.dataMessages[idx!][TypeDataMessage.progress] = 0.0
  4069. let section = self.dataDates.firstIndex(of: self.dataMessages[idx!]["chat_date"] as? String ?? "")
  4070. let row = self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == self.dataMessages[idx!]["chat_date"] as? String ?? ""}).firstIndex(where: { $0["message_id"] as? String == self.dataMessages[idx!]["message_id"] as? String })
  4071. if row != nil && section != nil {
  4072. self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
  4073. }
  4074. } catch {
  4075. }
  4076. }
  4077. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  4078. do {
  4079. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  4080. "status" : "1"
  4081. ], _where: "message_id = '\(messageId)'")
  4082. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE_STATUS", cvalues: [
  4083. "status" : "1"
  4084. ], _where: "message_id = '\(messageId)'")
  4085. } catch {
  4086. rollback.pointee = true
  4087. print("Access database error: \(error.localizedDescription)")
  4088. }
  4089. })
  4090. let message = CoreMessage_TMessageBank.sendMessage(message_id: messageId,
  4091. l_pin: dataMessages[indexPath!.row][TypeDataMessage.l_pin] as? String ?? "",
  4092. message_scope_id: dataMessages[indexPath!.row][TypeDataMessage.message_scope_id] as? String ?? "",
  4093. status: "1",
  4094. message_text: dataMessages[indexPath!.row][TypeDataMessage.message_text] as? String ?? "",
  4095. credential: dataMessages[indexPath!.row][TypeDataMessage.credential] as? String ?? "",
  4096. attachment_flag: dataMessages[indexPath!.row][TypeDataMessage.attachment_flag] as? String ?? "",
  4097. ex_blog_id: dataMessages[indexPath!.row][TypeDataMessage.blog_id] as? String ?? "",
  4098. message_large_text: "",
  4099. ex_format: "",
  4100. image_id: dataMessages[indexPath!.row][TypeDataMessage.image_id] as? String ?? "",
  4101. audio_id: dataMessages[indexPath!.row][TypeDataMessage.audio_id] as? String ?? "",
  4102. video_id: dataMessages[indexPath!.row][TypeDataMessage.video_id] as? String ?? "",
  4103. file_id: dataMessages[indexPath!.row][TypeDataMessage.file_id] as? String ?? "",
  4104. thumb_id: dataMessages[indexPath!.row][TypeDataMessage.thumb_id] as? String ?? "",
  4105. reff_id: dataMessages[indexPath!.row][TypeDataMessage.reff_id] as? String ?? "",
  4106. read_receipts: dataMessages[indexPath!.row][TypeDataMessage.read_receipts] as? String ?? "",
  4107. chat_id: dataMessages[indexPath!.row][TypeDataMessage.chat_id] as? String ?? "",
  4108. is_call_center: dataMessages[indexPath!.row][TypeDataMessage.is_call_center] as? String ?? "",
  4109. call_center_id: dataMessages[indexPath!.row][TypeDataMessage.call_center_id] as? String ?? "",
  4110. opposite_pin: dataMessages[indexPath!.row][TypeDataMessage.opposite_pin] as? String ?? "")
  4111. Nexilis.addQueueMessage(message: message)
  4112. })
  4113. var children: [UIMenuElement] = [star, reply, forward, copy, delete]
  4114. var isMore = false
  4115. // let copyOption = self.copyOption(indexPath: indexPath!)
  4116. let idMe = User.getMyPin() as String?
  4117. if dataMessages[indexPath!.row]["status"] as? String ?? "" == "0" {
  4118. children = [resend, delete]
  4119. } else if isContactCenter {
  4120. if dataMessages[indexPath!.row]["attachment_flag"] as? String ?? "" == "11" {
  4121. children = [reply]
  4122. }
  4123. else {
  4124. children = [reply, copy]
  4125. }
  4126. } else if (dataMessages[indexPath!.row]["lock"] != nil && dataMessages[indexPath!.row]["lock"] as? String ?? "" == "1") || dataMessages[indexPath!.row]["message_scope_id"] as? String ?? "" == "18" || dataPerson["f_pin"] == "-999" || dataMessages[indexPath!.row]["credential"] as? String ?? "" == "1" {
  4127. children = [delete]
  4128. } else if (groupImages[dataMessages[indexPath!.row]["message_id"] as? String ?? ""] != nil) {
  4129. forward.title = "Forward All".localized()
  4130. delete.title = "Delete All".localized()
  4131. children = [forward, delete]
  4132. } else if blocking == "1" || blocking == "-1" {
  4133. children = [star, forward, copy ,delete]
  4134. if !(dataMessages[indexPath!.row]["image_id"] as? String ?? "").isEmpty || !(dataMessages[indexPath!.row]["video_id"] as? String ?? "").isEmpty {
  4135. children = [star, forward ,delete]
  4136. }
  4137. if (dataMessages[indexPath!.row]["f_pin"] as? String ?? "") == idMe {
  4138. children.insert(info, at: children.count - 1)
  4139. }
  4140. }
  4141. else if !(dataMessages[indexPath!.row]["image_id"] as? String ?? "").isEmpty || !(dataMessages[indexPath!.row]["video_id"] as? String ?? "").isEmpty || !(dataMessages[indexPath!.row]["file_id"] as? String ?? "").isEmpty {
  4142. children = [star, reply, forward ,delete]
  4143. if (dataMessages[indexPath!.row]["f_pin"] as? String ?? "") == idMe {
  4144. children.insert(info, at: children.count - 1)
  4145. }
  4146. } else if dataMessages[indexPath!.row]["attachment_flag"] as? String ?? "" == "11" {
  4147. children = [reply, delete]
  4148. if (dataMessages[indexPath!.row]["f_pin"] as? String ?? "") == idMe {
  4149. children.insert(info, at: children.count - 1)
  4150. }
  4151. } else {
  4152. if (dataMessages[indexPath!.row]["f_pin"] as? String ?? "") == idMe {
  4153. children.insert(info, at: children.count - 1)
  4154. }
  4155. if !(dataMessages[indexPath!.row][TypeDataMessage.message_text] as? String ?? "").isEmpty {
  4156. if (dataMessages[indexPath!.row]["f_pin"] as? String ?? "") == idMe && ((dataMessages[indexPath!.row][TypeDataMessage.is_forwarded] as? Int) ?? 0) == 0 {
  4157. children.insert(edit, at: children.count - 1)
  4158. }
  4159. isMore = true
  4160. }
  4161. }
  4162. let mainMenu = UIMenu(title: "", options: [.displayInline],
  4163. children: children)
  4164. var menuForShow = UIMenu(title: "", children: [mainMenu])
  4165. if isMore {
  4166. menuForShow = UIMenu(title: "", children: [mainMenu, more])
  4167. }
  4168. return UIContextMenuConfiguration(identifier: nil,
  4169. previewProvider: nil) { _ in
  4170. return menuForShow
  4171. }
  4172. }
  4173. func showEditMessageView(at indexPath: IndexPath) {
  4174. let dataMessages = self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == dataDates[indexPath.section]})
  4175. let oldText = dataMessages[indexPath.row][TypeDataMessage.message_text] as? String ?? ""
  4176. editVC = UIViewController()
  4177. if let view = editVC.view {
  4178. view.backgroundColor = .clear
  4179. let blurView = UIView()
  4180. let blurEffect = UIBlurEffect(style: .systemUltraThinMaterialLight)
  4181. let blurEffectView = UIVisualEffectView(effect: blurEffect)
  4182. blurEffectView.frame = blurView.bounds
  4183. blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  4184. blurView.addSubview(blurEffectView)
  4185. blurView.sendSubviewToBack(blurEffectView)
  4186. view.addSubview(blurView)
  4187. blurView.anchor(top: view.topAnchor, left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor)
  4188. let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissEditVC))
  4189. tapGesture.cancelsTouchesInView = false
  4190. view.addGestureRecognizer(tapGesture)
  4191. editTextView = UITextView()
  4192. editTextView.layer.cornerRadius = textFieldSend.maxCornerRadius()
  4193. editTextView.layer.borderWidth = 1.0
  4194. editTextView.textColor = UIColor.black
  4195. editTextView.tintColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
  4196. editTextView.textContainerInset = UIEdgeInsets(top: 12, left: 20, bottom: 11, right: 40)
  4197. editTextView.layer.borderColor = UIColor.lightGray.withAlphaComponent(0.5).cgColor
  4198. editTextView.font = UIFont.systemFont(ofSize: 12)
  4199. editTextView.delegate = self
  4200. editTextView.allowsEditingTextAttributes = true
  4201. editTextView.backgroundColor = .clear
  4202. view.addSubview(editTextView)
  4203. editTextView.anchor(left: view.leftAnchor, right: view.rightAnchor, paddingLeft: 15, paddingRight: 15)
  4204. constraintBottomeditTextView = editTextView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -15)
  4205. constraintHeighteditTextView = editTextView.heightAnchor.constraint(equalToConstant: 40)
  4206. constraintBottomeditTextView.isActive = true
  4207. constraintHeighteditTextView.isActive = true
  4208. editTextView.text = oldText
  4209. editTextView.becomeFirstResponder()
  4210. let buttonSend = UIButton(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
  4211. buttonSend.setImage(resizeImage(image: self.traitCollection.userInterfaceStyle == .dark ? UIImage(named: "Send-(White)", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(.blackDarkMode) : UIImage(named: "Send-(White)", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, targetSize: CGSize(width: 30, height: 30)).withRenderingMode(.alwaysOriginal), for: .normal)
  4212. buttonSend.circle()
  4213. buttonSend.actionHandle(controlEvents: .touchUpInside,
  4214. ForAction:{() -> Void in
  4215. let newText = self.editTextView.text ?? ""
  4216. if !newText.isEmpty && newText.trimmingCharacters(in: .whitespacesAndNewlines) != oldText {
  4217. let lastEdited = Int64(Date().currentTimeMillis())
  4218. let message = CoreMessage_TMessageBank.editMessage(message_id: dataMessages[indexPath.row][TypeDataMessage.message_id] as? String ?? "", l_pin: dataMessages[indexPath.row][TypeDataMessage.l_pin] as? String ?? "", message_scope_id: dataMessages[indexPath.row][TypeDataMessage.message_scope_id] as? String ?? "", status: "1", message_text: newText, credential: dataMessages[indexPath.row][TypeDataMessage.credential] as? String ?? "", attachment_flag: dataMessages[indexPath.row][TypeDataMessage.attachment_flag] as? String ?? "", ex_blog_id: dataMessages[indexPath.row][TypeDataMessage.blog_id] as? String ?? "", message_large_text: "", ex_format: "", image_id: dataMessages[indexPath.row][TypeDataMessage.image_id] as? String ?? "", audio_id: dataMessages[indexPath.row][TypeDataMessage.audio_id] as? String ?? "", video_id: dataMessages[indexPath.row][TypeDataMessage.video_id] as? String ?? "", file_id: dataMessages[indexPath.row][TypeDataMessage.file_id] as? String ?? "", thumb_id: dataMessages[indexPath.row][TypeDataMessage.thumb_id] as? String ?? "", reff_id: dataMessages[indexPath.row][TypeDataMessage.reff_id] as? String ?? "", read_receipts: dataMessages[indexPath.row][TypeDataMessage.read_receipts] as? String ?? "", chat_id: dataMessages[indexPath.row][TypeDataMessage.chat_id] as? String ?? "", is_call_center: dataMessages[indexPath.row][TypeDataMessage.is_call_center] as? String ?? "", call_center_id: dataMessages[indexPath.row][TypeDataMessage.call_center_id] as? String ?? "", opposite_pin: dataMessages[indexPath.row][TypeDataMessage.opposite_pin] as? String ?? "", last_edit: lastEdited)
  4219. Nexilis.addQueueMessage(message: message, isEditMessage: true)
  4220. DispatchQueue.global().async {
  4221. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  4222. do {
  4223. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  4224. "message_text" : newText,
  4225. "last_edited" : lastEdited
  4226. ], _where: "message_id = '\(dataMessages[indexPath.row]["message_id"] as? String ?? "")'")
  4227. NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
  4228. } catch {
  4229. rollback.pointee = true
  4230. print("Access database error: \(error.localizedDescription)")
  4231. }
  4232. })
  4233. }
  4234. let idx = self.dataMessages.firstIndex(where: { $0[TypeDataMessage.message_id] as? String == dataMessages[indexPath.row][TypeDataMessage.message_id] as? String})
  4235. if idx != nil{
  4236. self.dataMessages[idx!][TypeDataMessage.message_text] = newText
  4237. self.dataMessages[idx!][TypeDataMessage.last_edit] = lastEdited
  4238. self.tableChatView.reloadRows(at: [indexPath], with: .none)
  4239. }
  4240. }
  4241. })
  4242. buttonSend.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .mainColor
  4243. view.addSubview(buttonSend)
  4244. buttonSend.anchor(right: view.rightAnchor, paddingRight: 15, width: 40, height: 40)
  4245. constraintBottomSendEditTV = buttonSend.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -15)
  4246. constraintBottomSendEditTV.isActive = true
  4247. let viewMessage = UIView()
  4248. view.addSubview(viewMessage)
  4249. viewMessage.translatesAutoresizingMaskIntoConstraints = false
  4250. if (dataMessages[indexPath.row][TypeDataMessage.f_pin] as? String == User.getMyPin()) {
  4251. viewMessage.leftAnchor.constraint(greaterThanOrEqualTo: view.leftAnchor, constant: 60).isActive = true
  4252. viewMessage.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -15).isActive = true
  4253. viewMessage.backgroundColor = .blueBubbleColor
  4254. viewMessage.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner, .layerMinXMinYCorner]
  4255. } else {
  4256. viewMessage.rightAnchor.constraint(lessThanOrEqualTo: view.rightAnchor, constant: 60).isActive = true
  4257. viewMessage.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 15).isActive = true
  4258. viewMessage.backgroundColor = .whiteBubbleColor
  4259. viewMessage.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMinYCorner, .layerMaxXMaxYCorner]
  4260. }
  4261. viewMessage.bottomAnchor.constraint(equalTo: editTextView.topAnchor, constant: -15).isActive = true
  4262. viewMessage.heightAnchor.constraint(greaterThanOrEqualToConstant: 44).isActive = true
  4263. viewMessage.widthAnchor.constraint(greaterThanOrEqualToConstant: 46).isActive = true
  4264. viewMessage.layer.cornerRadius = 10.0
  4265. viewMessage.clipsToBounds = true
  4266. let messageText = UILabel()
  4267. messageText.numberOfLines = 0
  4268. messageText.lineBreakMode = .byWordWrapping
  4269. viewMessage.addSubview(messageText)
  4270. messageText.translatesAutoresizingMaskIntoConstraints = false
  4271. messageText.topAnchor.constraint(equalTo: viewMessage.topAnchor, constant: 15).isActive = true
  4272. messageText.leadingAnchor.constraint(equalTo: viewMessage.leadingAnchor, constant: 15).isActive = true
  4273. messageText.bottomAnchor.constraint(equalTo: viewMessage.bottomAnchor, constant: -15).isActive = true
  4274. messageText.trailingAnchor.constraint(equalTo: viewMessage.trailingAnchor, constant: -15).isActive = true
  4275. messageText.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
  4276. messageText.font = .systemFont(ofSize: 12)
  4277. messageText.text = oldText
  4278. }
  4279. editVC.modalTransitionStyle = .crossDissolve
  4280. editVC.modalPresentationStyle = .overFullScreen
  4281. self.present(editVC, animated: true, completion: {
  4282. self.constraintHeighteditTextView.constant = self.editTextView.contentSize.height
  4283. if self.constraintHeighteditTextView.constant > 95 {
  4284. self.constraintHeighteditTextView.constant = 95.0
  4285. }
  4286. })
  4287. }
  4288. @objc func dismissEditVC() {
  4289. self.isEditingMessage = false
  4290. editVC.dismiss(animated: true)
  4291. }
  4292. @objc func cancelAction() {
  4293. DispatchQueue.main.async {
  4294. if self.copySession {
  4295. self.copySession = false
  4296. } else if self.forwardSession {
  4297. self.forwardSession = false
  4298. } else if self.deleteSession {
  4299. self.deleteSession = false
  4300. } else if self.isSearching {
  4301. self.countMatchesSearch = 0
  4302. self.isSearching = false
  4303. }
  4304. if self.viewTextfield.isHidden {
  4305. self.viewTextfield.isHidden = false
  4306. }
  4307. if self.viewAttachment.isHidden {
  4308. self.viewAttachment.isHidden = false
  4309. }
  4310. if self.containerAction.isHidden {
  4311. self.containerAction.isHidden = false
  4312. }
  4313. if self.viewButton.isHidden {
  4314. self.viewButton.isHidden = false
  4315. }
  4316. if self.constraintBottomTableViewWithTextfield.constant == -60.0 {
  4317. self.constraintBottomTableViewWithTextfield.constant = self.constraintBottomTableViewWithTextfield.constant + 70
  4318. DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: {
  4319. if (self.currentIndexpath != nil) {
  4320. self.tableChatView.scrollToRow(at: IndexPath(row: self.currentIndexpath!.row, section: self.currentIndexpath!.section), at: .none, animated: true)
  4321. } else {
  4322. self.tableChatView.scrollToBottom()
  4323. }
  4324. })
  4325. }
  4326. let data = self.dataMessages.filter({ $0["isSelected"] as? Bool == true })
  4327. for i in 0..<data.count {
  4328. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == data[i]["message_id"] as? String})
  4329. if idx != nil{
  4330. self.dataMessages[idx!]["isSelected"] = false
  4331. }
  4332. }
  4333. self.tableChatView.reloadData()
  4334. self.setRightButtonItem()
  4335. self.changeAppBar()
  4336. if self.isContactCenter || self.fromNotification {
  4337. let backButton = UIBarButtonItem(image: UIImage(systemName: "chevron.backward"), style: .plain, target: self, action: #selector(self.didTapExit))
  4338. self.navigationItem.leftBarButtonItem = backButton
  4339. }
  4340. self.containerMultpileSelectSession.removeFromSuperview()
  4341. self.checkNewMessage(tableView: self.tableChatView)
  4342. }
  4343. }
  4344. private func addMultipleSelectSession() {
  4345. viewTextfield.isHidden = true
  4346. viewAttachment.isHidden = true
  4347. containerAction.isHidden = true
  4348. viewButton.isHidden = true
  4349. constraintBottomTableViewWithTextfield.constant = constraintBottomTableViewWithTextfield.constant - 70
  4350. view.addSubview(containerMultpileSelectSession)
  4351. containerMultpileSelectSession.translatesAutoresizingMaskIntoConstraints = false
  4352. constraintBottomContainerMultpileSelectSession = containerMultpileSelectSession.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0)
  4353. NSLayoutConstraint.activate([
  4354. containerMultpileSelectSession.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
  4355. containerMultpileSelectSession.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
  4356. constraintBottomContainerMultpileSelectSession,
  4357. containerMultpileSelectSession.heightAnchor.constraint(equalToConstant: 50)
  4358. ])
  4359. containerMultpileSelectSession.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : .white
  4360. addSubviewMultipleSession()
  4361. }
  4362. private func addSubviewMultipleSession() {
  4363. let container = UIView()
  4364. containerMultpileSelectSession.addSubview(container)
  4365. container.translatesAutoresizingMaskIntoConstraints = false
  4366. NSLayoutConstraint.activate([
  4367. container.leadingAnchor.constraint(equalTo: containerMultpileSelectSession.leadingAnchor),
  4368. container.trailingAnchor.constraint(equalTo:containerMultpileSelectSession.trailingAnchor),
  4369. container.bottomAnchor.constraint(equalTo: containerMultpileSelectSession.bottomAnchor),
  4370. container.heightAnchor.constraint(equalToConstant: 50)
  4371. ])
  4372. container.layer.shadowOpacity = 0.7
  4373. container.layer.shadowOffset = CGSize(width: 3, height: 3)
  4374. container.layer.shadowRadius = 3.0
  4375. container.layer.shadowColor = UIColor.black.cgColor
  4376. container.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : .secondaryColor
  4377. if !isSearching {
  4378. let title = UILabel()
  4379. container.addSubview(title)
  4380. title.translatesAutoresizingMaskIntoConstraints = false
  4381. NSLayoutConstraint.activate([
  4382. title.centerXAnchor.constraint(equalTo: container.centerXAnchor),
  4383. title.centerYAnchor.constraint(equalTo:container.centerYAnchor),
  4384. ])
  4385. let countSelected = dataMessages.filter({ $0["isSelected"] as? Bool == true }).count
  4386. title.text = "\(countSelected) " + "Selected".localized()
  4387. title.textColor = .mainColor
  4388. title.font = UIFont.systemFont(ofSize: 15.0).bold
  4389. let button = UIImageView()
  4390. container.addSubview(button)
  4391. button.translatesAutoresizingMaskIntoConstraints = false
  4392. NSLayoutConstraint.activate([
  4393. button.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 15),
  4394. button.centerYAnchor.constraint(equalTo:container.centerYAnchor),
  4395. button.widthAnchor.constraint(equalToConstant: 30),
  4396. button.heightAnchor.constraint(equalToConstant: 30),
  4397. ])
  4398. if copySession {
  4399. button.image = UIImage(systemName: "doc.on.doc")
  4400. if countSelected == 0 {
  4401. button.tintColor = .gray
  4402. } else {
  4403. button.tintColor = .mainColor
  4404. }
  4405. } else if forwardSession {
  4406. button.image = UIImage(systemName: "arrowshape.turn.up.right")
  4407. if countSelected == 0 {
  4408. button.tintColor = .gray
  4409. } else {
  4410. button.tintColor = .mainColor
  4411. }
  4412. } else if deleteSession {
  4413. button.image = UIImage(systemName: "trash")
  4414. if countSelected == 0 {
  4415. button.tintColor = .gray
  4416. } else {
  4417. button.tintColor = .red
  4418. }
  4419. }
  4420. let buttonGesture = UITapGestureRecognizer(target: self, action: #selector(sessionAction))
  4421. button.isUserInteractionEnabled = true
  4422. button.addGestureRecognizer(buttonGesture)
  4423. let selectedMessage = dataMessages.filter({ $0["isSelected"] as? Bool == true })
  4424. if selectedMessage.count > 0 {
  4425. for i in 0..<selectedMessage.count {
  4426. if let isGroupingImages = groupImages[selectedMessage[i]["message_id"] as? String ?? ""] {
  4427. title.text = "\(countSelected + (isGroupingImages.count - 1)) " + "Selected".localized()
  4428. }
  4429. }
  4430. }
  4431. } else {
  4432. buttonUp = UIButton()
  4433. container.addSubview(buttonUp)
  4434. buttonUp.translatesAutoresizingMaskIntoConstraints = false
  4435. NSLayoutConstraint.activate([
  4436. buttonUp.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 10),
  4437. buttonUp.centerYAnchor.constraint(equalTo:container.centerYAnchor),
  4438. buttonUp.widthAnchor.constraint(equalToConstant: 30),
  4439. buttonUp.heightAnchor.constraint(equalToConstant: 30),
  4440. ])
  4441. buttonUp.addTarget(self, action: #selector(upSearchText), for: .touchUpInside)
  4442. buttonDown = UIButton()
  4443. container.addSubview(buttonDown)
  4444. buttonDown.translatesAutoresizingMaskIntoConstraints = false
  4445. NSLayoutConstraint.activate([
  4446. buttonDown.leadingAnchor.constraint(equalTo: buttonUp.trailingAnchor, constant: 15),
  4447. buttonDown.centerYAnchor.constraint(equalTo:container.centerYAnchor),
  4448. buttonDown.widthAnchor.constraint(equalToConstant: 30),
  4449. buttonDown.heightAnchor.constraint(equalToConstant: 30),
  4450. ])
  4451. buttonDown.addTarget(self, action: #selector(downSearchText), for: .touchUpInside)
  4452. buttonUp.setImage(UIImage(systemName: "chevron.up"), for: .normal)
  4453. buttonUp.tintColor = .gray
  4454. buttonDown.setImage(UIImage(systemName: "chevron.down"), for: .normal)
  4455. buttonDown.tintColor = .gray
  4456. titleSearchMatches = UILabel()
  4457. container.addSubview(titleSearchMatches)
  4458. titleSearchMatches.translatesAutoresizingMaskIntoConstraints = false
  4459. NSLayoutConstraint.activate([
  4460. titleSearchMatches.centerXAnchor.constraint(equalTo: container.centerXAnchor),
  4461. titleSearchMatches.centerYAnchor.constraint(equalTo:container.centerYAnchor),
  4462. ])
  4463. titleSearchMatches.textColor = .mainColor
  4464. titleSearchMatches.font = UIFont.systemFont(ofSize: 15.0).bold
  4465. titleSearchMatches.isHidden = true
  4466. DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: {
  4467. self.searchBar.becomeFirstResponder()
  4468. })
  4469. }
  4470. }
  4471. @objc func upSearchText() {
  4472. scrollToFirstSearchMessage(indexScroll: lastScrollIdxSearch + 1)
  4473. }
  4474. @objc func downSearchText() {
  4475. scrollToFirstSearchMessage(indexScroll: lastScrollIdxSearch - 1)
  4476. }
  4477. @objc func sessionAction() {
  4478. if copySession {
  4479. let dataMessages = self.dataMessages.filter({ $0["isSelected"] as? Bool == true })
  4480. let countSelected = dataMessages.count
  4481. if countSelected == 0 {
  4482. return
  4483. }
  4484. var text = ""
  4485. for i in 0..<countSelected {
  4486. let stringDate = (dataMessages[i]["server_date"] as? String ?? "")
  4487. let date = Date(milliseconds: Int64(stringDate)!)
  4488. let formatterDate = DateFormatter()
  4489. let formatterTime = DateFormatter()
  4490. formatterDate.dateFormat = "dd/MM/yy"
  4491. formatterDate.locale = NSLocale(localeIdentifier: "id") as Locale?
  4492. formatterTime.dateFormat = "HH:mm"
  4493. formatterTime.locale = NSLocale(localeIdentifier: "id") as Locale?
  4494. let dataProfile = getDataProfile(message_id: dataMessages[i]["message_id"] as? String ?? "")
  4495. if text.isEmpty {
  4496. text = "*[\(formatterDate.string(from: date as Date)) \(formatterTime.string(from: date as Date))] \(dataProfile["name"]!):*\n\(dataMessages[i]["message_text"] as? String ?? "")"
  4497. } else {
  4498. text = text + "\n\n*[\(formatterDate.string(from: date as Date)) \(formatterTime.string(from: date as Date))] \(dataProfile["name"]!):*\n\(dataMessages[i]["message_text"] as? String ?? "")"
  4499. }
  4500. }
  4501. text = text + "\n\n\nchat " + "Powered by Nexilis".localized()
  4502. DispatchQueue.main.async {
  4503. UIPasteboard.general.string = text
  4504. self.view.makeToast("Text coppied to clipboard".localized(), duration: 3)
  4505. }
  4506. cancelAction()
  4507. } else if forwardSession {
  4508. var dataMessages = self.dataMessages.filter({ $0["isSelected"] as! Bool == true })
  4509. let countSelected = dataMessages.count
  4510. if countSelected == 0 {
  4511. return
  4512. }
  4513. for i in 0..<countSelected {
  4514. if groupImages[dataMessages[i]["message_id"] as? String ?? ""] != nil {
  4515. var tempData = dataMessages
  4516. tempData.remove(at: 0)
  4517. let dataMessageInGrouping = (groupImages[dataMessages[i]["message_id"] as? String ?? ""]!).map({ $0.dataMessage })
  4518. tempData.insert(contentsOf: dataMessageInGrouping, at: i)
  4519. dataMessages = tempData
  4520. }
  4521. }
  4522. contactChatNav.modalPresentationStyle = .custom
  4523. contactChatNav.navigationBar.tintColor = .white
  4524. contactChatNav.navigationBar.barTintColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : .mainColor
  4525. contactChatNav.navigationBar.isTranslucent = false
  4526. let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
  4527. contactChatNav.navigationBar.titleTextAttributes = textAttributes
  4528. contactChatNav.view.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : .mainColor
  4529. if let controller = contactChatNav.viewControllers.first as? ContactChatViewController {
  4530. controller.isChooser = { [weak self] scope, pin in
  4531. if scope == "3" {
  4532. let editorPersonalVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorPersonalVC") as! EditorPersonal
  4533. editorPersonalVC.unique_l_pin = pin
  4534. editorPersonalVC.dataMessageForward = dataMessages
  4535. self?.navigationController?.replaceAllViewController(with: editorPersonalVC, animated: true)
  4536. } else {
  4537. let editorGroupVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorGroupVC") as! EditorGroup
  4538. editorGroupVC.unique_l_pin = pin
  4539. editorGroupVC.dataMessageForward = dataMessages
  4540. self?.navigationController?.replaceAllViewController(with: editorGroupVC, animated: true)
  4541. }
  4542. }
  4543. }
  4544. self.present(contactChatNav, animated: true, completion: nil)
  4545. } else if deleteSession {
  4546. let dataMessages = self.dataMessages.filter({ $0["isSelected"] as! Bool == true })
  4547. var countSelected = dataMessages.count
  4548. if countSelected == 0 {
  4549. return
  4550. }
  4551. for i in 0..<countSelected {
  4552. if let isGroupingImages = groupImages[dataMessages[i]["message_id"] as? String ?? ""] {
  4553. countSelected += (isGroupingImages.count - 1)
  4554. }
  4555. }
  4556. let alertController = LibAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
  4557. if let action = self.actionDelete(for: "me", title: "Delete".localized() + " \(countSelected) " + "For Me".localized(), dataMessages: dataMessages) {
  4558. alertController.addAction(action)
  4559. }
  4560. let idMe = User.getMyPin() as String?
  4561. let dataFilterFpin = dataMessages.filter({ $0["l_pin"] as? String == idMe})
  4562. let dataFilterLock = dataMessages.filter({ $0["lock"] as? String == "1" || $0["lock"] as? String == "2" })
  4563. // let statusDataRead = dataMessages.filter({ Int($0["status"] as? String ?? "")! >= 4})
  4564. let statusFailed = dataMessages.filter({ Int($0["status"] as? String ?? "")! == 0})
  4565. if dataFilterFpin.count == 0 && dataFilterLock.count == 0 && statusFailed.count == 0 {
  4566. if let action = self.actionDelete(for: "everyone", title: "Delete".localized() + " \(countSelected) " + "For Everyone".localized(), dataMessages: dataMessages) {
  4567. alertController.addAction(action)
  4568. }
  4569. }
  4570. alertController.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: nil))
  4571. self.present(alertController, animated: true)
  4572. }
  4573. }
  4574. private func getDataProfile(message_id: String) -> [String: String]{
  4575. var data: [String: String] = [:]
  4576. Database().database?.inTransaction({ fmdb, rollback in
  4577. if let c = Database().getRecords(fmdb: fmdb, query: "select f_display_name from MESSAGE where message_id = '\(message_id)'"), c.next() {
  4578. data["name"] = c.string(forColumnIndex: 0)!
  4579. c.close()
  4580. } else {
  4581. data["name"] = "Unknown".localized()
  4582. data["image_id"] = ""
  4583. }
  4584. })
  4585. return data
  4586. }
  4587. private func deleteMessage(l_pin: String, message_id: String, scope: String, type: String, chat: String) {
  4588. let tmessage = CoreMessage_TMessageBank.deleteMessage(l_pin: l_pin, messageId: message_id, scope: scope, type: type, chat: chat)
  4589. Nexilis.deleteQueueMessage(message: tmessage)
  4590. }
  4591. private func queryMessageReply(message_id: String) -> [String: Any?] {
  4592. var dataQuery: [String: Any] = [:]
  4593. Database().database?.inTransaction({ fmdb, rollback in
  4594. if let c = Database().getRecords(fmdb: fmdb, query: "SELECT message_id, f_pin, message_text, attachment_flag, thumb_id, image_id, video_id, file_id FROM MESSAGE where message_id='\(message_id)'"), c.next() {
  4595. dataQuery["message_id"] = c.string(forColumnIndex: 0) ?? ""
  4596. dataQuery["f_pin"] = c.string(forColumnIndex: 1) ?? ""
  4597. dataQuery["message_text"] = c.string(forColumnIndex: 2) ?? ""
  4598. dataQuery["attachment_flag"] = c.string(forColumnIndex: 3) ?? ""
  4599. dataQuery["thumb_id"] = c.string(forColumnIndex: 4) ?? ""
  4600. dataQuery["image_id"] = c.string(forColumnIndex: 5) ?? ""
  4601. dataQuery["video_id"] = c.string(forColumnIndex: 6) ?? ""
  4602. dataQuery["file_id"] = c.string(forColumnIndex: 7) ?? ""
  4603. c.close()
  4604. }
  4605. })
  4606. return dataQuery
  4607. }
  4608. @objc func segmentedControlValueChanged(_ sender: segmentedControllerObject) {
  4609. switch sender.selectedSegmentIndex {
  4610. case 0:
  4611. sender.navigation.viewControllers[0].children[1].view.isHidden = true
  4612. break;
  4613. case 1:
  4614. sender.navigation.viewControllers[0].children[1].view.isHidden = false
  4615. break;
  4616. default:
  4617. break;
  4618. }
  4619. }
  4620. private func copyOption(indexPath: IndexPath) -> UIMenu {
  4621. var ratingButtonTitles = ["Text".localized(), "Image".localized()]
  4622. if (dataMessages[indexPath.row]["message_text"] as? String ?? "").isEmpty {
  4623. ratingButtonTitles = ["Image".localized()]
  4624. }
  4625. let dataMessages = self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == dataDates[indexPath.section]})
  4626. let copyActions = ratingButtonTitles
  4627. .enumerated()
  4628. .map { index, title in
  4629. return UIAction(
  4630. title: title,
  4631. identifier: nil,
  4632. handler: {(_) in
  4633. if (dataMessages[indexPath.row]["message_text"] as? String ?? "").isEmpty {
  4634. DispatchQueue.main.async {
  4635. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  4636. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  4637. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  4638. if let dirPath = paths.first {
  4639. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(dataMessages[indexPath.row]["image_id"] as? String ?? "")
  4640. if FileManager.default.fileExists(atPath: imageURL.path) {
  4641. let image = UIImage(contentsOfFile: imageURL.path)
  4642. UIPasteboard.general.image = image
  4643. self.view.makeToast("Image coppied to clipboard".localized(), duration: 3)
  4644. } else if FileEncryption.shared.isSecureExists(filename: imageURL.lastPathComponent) {
  4645. do {
  4646. if let imageData = try FileEncryption.shared.readSecure(filename: imageURL.lastPathComponent) {
  4647. let image = UIImage(data: imageData)
  4648. UIPasteboard.general.image = image
  4649. self.view.makeToast("Image coppied to clipboard".localized(), duration: 3)
  4650. }
  4651. } catch {
  4652. }
  4653. }
  4654. }
  4655. }
  4656. return
  4657. }
  4658. if (index == 0) {
  4659. DispatchQueue.main.async {
  4660. UIPasteboard.general.string = dataMessages[indexPath.row]["message_text"] as? String
  4661. self.view.makeToast("Text coppied to clipboard".localized(), duration: 3)
  4662. }
  4663. } else {
  4664. DispatchQueue.main.async {
  4665. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  4666. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  4667. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  4668. if let dirPath = paths.first {
  4669. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(dataMessages[indexPath.row]["image_id"] as? String ?? "")
  4670. if FileManager.default.fileExists(atPath: imageURL.path) {
  4671. let image = UIImage(contentsOfFile: imageURL.path)
  4672. UIPasteboard.general.image = image
  4673. self.view.makeToast("Image coppied to clipboard".localized(), duration: 3)
  4674. } else if FileEncryption.shared.isSecureExists(filename: imageURL.lastPathComponent) {
  4675. do {
  4676. if let imageData = try FileEncryption.shared.readSecure(filename: imageURL.lastPathComponent) {
  4677. let image = UIImage(data: imageData)
  4678. UIPasteboard.general.image = image
  4679. self.view.makeToast("Image coppied to clipboard".localized(), duration: 3)
  4680. }
  4681. } catch {
  4682. }
  4683. }
  4684. }
  4685. }
  4686. }
  4687. self.dismissKeyboard()
  4688. })
  4689. }
  4690. return UIMenu(
  4691. title: "Copy".localized(),
  4692. image: UIImage(systemName: "doc.on.doc.fill"),
  4693. children: copyActions)
  4694. }
  4695. private func actionDelete(for type: String, title: String, dataMessages: [[String: Any?]]) -> UIAlertAction? {
  4696. return UIAlertAction(title: title, style: .destructive) { [unowned self] _ in
  4697. for i in 0..<dataMessages.count {
  4698. if (type == "me") {
  4699. if let groupingImages = groupImages[dataMessages[i]["message_id"] as? String ?? ""] {
  4700. for i in 0..<groupingImages.count {
  4701. self.deleteMessage(l_pin: groupingImages[i].lPin, message_id: groupingImages[i].messageId, scope: "3", type: "1", chat: "")
  4702. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == groupingImages[i].messageId })
  4703. if idx != nil {
  4704. self.dataMessages.remove(at: idx!)
  4705. if (idx == self.dataMessages.count - 1) {
  4706. NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
  4707. }
  4708. for i in 0..<dataDates.count {
  4709. if self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == dataDates[i] }).count == 0 {
  4710. dataDates.remove(at: i)
  4711. }
  4712. }
  4713. }
  4714. }
  4715. self.groupImages.removeValue(forKey: groupingImages[0].messageId)
  4716. } else {
  4717. self.deleteMessage(l_pin: dataMessages[i]["l_pin"] as? String ?? "", message_id: dataMessages[i]["message_id"] as? String ?? "", scope: "3", type: "1", chat: "")
  4718. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[i]["message_id"] as? String})
  4719. if idx != nil {
  4720. self.dataMessages.remove(at: idx!)
  4721. if (idx == self.dataMessages.count - 1) {
  4722. NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
  4723. }
  4724. for i in 0..<dataDates.count {
  4725. if self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == dataDates[i] }).count == 0 {
  4726. dataDates.remove(at: i)
  4727. }
  4728. }
  4729. }
  4730. }
  4731. } else {
  4732. if !CheckConnection.isConnectedToNetwork() || API.nGetCLXConnState() == 0 {
  4733. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  4734. imageView.tintColor = .white
  4735. let banner = FloatingNotificationBanner(title: "Check your connection".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
  4736. banner.show()
  4737. } else {
  4738. if let groupingImages = groupImages[dataMessages[i]["message_id"] as? String ?? ""] {
  4739. for i in 0..<groupingImages.count {
  4740. self.deleteMessage(l_pin: groupingImages[i].lPin, message_id: groupingImages[i].messageId, scope: "3", type: "2", chat: "")
  4741. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == groupingImages[i].messageId})
  4742. if idx != nil {
  4743. self.dataMessages[idx!]["lock"] = "1"
  4744. self.dataMessages[idx!]["attachment_flag"] = "0"
  4745. self.dataMessages[idx!]["reff_id"] = ""
  4746. }
  4747. }
  4748. if let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == groupingImages[0].messageId}) {
  4749. var dataMessageInGrouping = (groupImages[dataMessages[i]["message_id"] as? String ?? ""]!).map({ $0.dataMessage })
  4750. dataMessageInGrouping.remove(at: 0)
  4751. self.dataMessages.insert(contentsOf: dataMessageInGrouping, at: idx+1)
  4752. self.groupImages.removeValue(forKey: groupingImages[0].messageId)
  4753. }
  4754. } else {
  4755. self.deleteMessage(l_pin: dataMessages[i]["l_pin"] as? String ?? "", message_id: dataMessages[i]["message_id"] as? String ?? "", scope: "3", type: "2", chat: "")
  4756. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[i]["message_id"] as? String})
  4757. if idx != nil {
  4758. self.dataMessages[idx!]["lock"] = "1"
  4759. self.dataMessages[idx!]["attachment_flag"] = "0"
  4760. self.dataMessages[idx!]["reff_id"] = ""
  4761. }
  4762. }
  4763. }
  4764. }
  4765. if self.listTimerCredential[dataMessages[i]["message_id"] as? String ?? ""] != nil {
  4766. self.listTimerCredential.removeValue(forKey: dataMessages[i]["message_id"] as? String ?? "")
  4767. self.timerCredential[dataMessages[i]["message_id"] as? String ?? ""]?.invalidate()
  4768. self.timerCredential.removeValue(forKey: dataMessages[i]["message_id"] as? String ?? "")
  4769. }
  4770. }
  4771. NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
  4772. cancelAction()
  4773. }
  4774. }
  4775. private func updateProfile() {
  4776. let idMe = User.getMyPin() as String?
  4777. DispatchQueue.global().async {
  4778. let message = CoreMessage_TMessageBank.getBatchBuddiesInfos(p_f_pin: idMe!, last_update: 0)
  4779. let _ = Nexilis.write(message: message)
  4780. }
  4781. }
  4782. private func generateQRCode(from string: String) -> UIImage? {
  4783. let data = string.data(using: String.Encoding.ascii)
  4784. if let filter = CIFilter(name: "CIQRCodeGenerator") {
  4785. filter.setValue(data, forKey: "inputMessage")
  4786. let transform = CGAffineTransform(scaleX: 3, y: 3)
  4787. if let output = filter.outputImage?.transformed(by: transform) {
  4788. return UIImage(ciImage: output)
  4789. }
  4790. }
  4791. return nil
  4792. }
  4793. @objc func deleteReplyView() {
  4794. if self.containerPreviewReply.isDescendant(of: self.viewTextfield) {
  4795. self.containerPreviewReply.subviews.forEach { $0.removeFromSuperview() }
  4796. self.containerPreviewReply.removeConstraints(self.containerPreviewReply.constraints)
  4797. self.containerPreviewReply.removeFromSuperview()
  4798. self.reffId = nil
  4799. UIView.animate(withDuration: 0.25, delay: 0.0, options: .curveEaseInOut, animations: {
  4800. self.constraintTopTextField.constant = self.constraintTopTextField.constant - 50
  4801. }, completion: nil)
  4802. }
  4803. }
  4804. @objc func removeLinkPreviewUntilEmptyTextView() {
  4805. isAlwaysHideLinkPreview = true
  4806. deleteLinkPreview()
  4807. }
  4808. @objc func deleteLinkPreview() {
  4809. if self.containerLink.isDescendant(of: self.viewTextfield) {
  4810. self.containerLink.subviews.forEach { $0.removeFromSuperview() }
  4811. self.containerLink.removeConstraints(self.containerLink.constraints)
  4812. self.containerLink.removeFromSuperview()
  4813. UIView.animate(withDuration: 0.25, delay: 0.0, options: .curveEaseInOut, animations: {
  4814. self.constraintTopTextField.constant = self.constraintTopTextField.constant - 80
  4815. }, completion: nil)
  4816. self.showingLink = ""
  4817. }
  4818. if self.reffId != nil {
  4819. self.bottomAnchorPreviewReply.isActive = false
  4820. self.bottomAnchorPreviewReply = self.containerPreviewReply.bottomAnchor.constraint(equalTo: self.textFieldSend.topAnchor)
  4821. self.bottomAnchorPreviewReply.isActive = true
  4822. }
  4823. }
  4824. }
  4825. //ECL
  4826. extension EditorPersonal: UICollectionViewDelegate, UICollectionViewDataSource {
  4827. public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
  4828. return 76
  4829. }
  4830. public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
  4831. let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellSticker", for: indexPath)
  4832. if (cell.contentView.subviews.count > 0) {
  4833. cell.contentView.subviews.forEach({ $0.removeFromSuperview() })
  4834. }
  4835. let imageSticker = UIImageView()
  4836. cell.contentView.addSubview(imageSticker)
  4837. imageSticker.translatesAutoresizingMaskIntoConstraints = false
  4838. NSLayoutConstraint.activate([
  4839. imageSticker.topAnchor.constraint(equalTo: cell.contentView.topAnchor),
  4840. imageSticker.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor),
  4841. imageSticker.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor),
  4842. imageSticker.trailingAnchor.constraint(equalTo: cell.contentView.trailingAnchor)
  4843. ])
  4844. var imageStickerBundle = UIImage(named: stickers[indexPath.row], in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  4845. if imageStickerBundle == nil {
  4846. imageStickerBundle = UIImage(named: stickers[indexPath.row], in: Bundle.resourcesMediaBundle(for: Nexilis.self), with: nil)
  4847. }
  4848. imageSticker.image = imageStickerBundle //resourcesMediaBundle
  4849. return cell
  4850. }
  4851. public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
  4852. sendChat(message_text: "sticker/\(stickers[indexPath.row])", attachment_flag: "11", viewController: self)
  4853. constraintBottomAttachment.constant = 0.0
  4854. self.viewSticker.removeConstraints(self.viewSticker.constraints)
  4855. self.viewSticker.removeFromSuperview()
  4856. }
  4857. }
  4858. //ETB
  4859. extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
  4860. // public func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
  4861. // checkNewMessage(tableView: tableView)
  4862. // }
  4863. public func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
  4864. if self.tableChatView.alpha != 1.0 {
  4865. UIView.animate(withDuration: 0.5, animations: {
  4866. self.tableChatView.alpha = 1.0
  4867. })
  4868. }
  4869. }
  4870. public func scrollViewDidScroll(_ scrollView: UIScrollView) {
  4871. lastY = scrollView.contentOffset.y
  4872. DispatchQueue.main.async { [self] in
  4873. checkNewMessage(tableView: self.tableChatView)
  4874. }
  4875. }
  4876. public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
  4877. if isContactCenter && indexPath.row == 0 && isRequestContactCenter {
  4878. return
  4879. }
  4880. let dataMessages = self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == dataDates[indexPath.section] })
  4881. if copySession || forwardSession || deleteSession {
  4882. if (dataMessages[indexPath.row]["attachment_flag"] as? String ?? "" != "0" || dataMessages[indexPath.row]["lock"] as? String == "1") && !forwardSession && !deleteSession {
  4883. return
  4884. }
  4885. if !(dataMessages[indexPath.row]["image_id"] as? String ?? "").isEmpty || !(dataMessages[indexPath.row]["video_id"] as? String ?? "").isEmpty || !(dataMessages[indexPath.row]["file_id"] as? String ?? "").isEmpty {
  4886. var file = dataMessages[indexPath.row]["image_id"] as? String ?? ""
  4887. if file.isEmpty {
  4888. file = dataMessages[indexPath.row]["video_id"] as? String ?? ""
  4889. if file.isEmpty {
  4890. file = dataMessages[indexPath.row]["file_id"] as? String ?? ""
  4891. }
  4892. }
  4893. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  4894. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  4895. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  4896. if let dirPath = paths.first {
  4897. let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(file)
  4898. if !FileManager.default.fileExists(atPath: fileURL.path) && !FileEncryption.shared.isSecureExists(filename: fileURL.lastPathComponent) {
  4899. return
  4900. }
  4901. }
  4902. }
  4903. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[indexPath.row]["message_id"] as? String})
  4904. if idx != nil {
  4905. self.dataMessages[idx!]["isSelected"] = !(self.dataMessages[idx!]["isSelected"] as! Bool)
  4906. self.tableChatView.reloadRows(at: [indexPath], with: .none)
  4907. }
  4908. containerMultpileSelectSession.subviews.forEach({ $0.removeFromSuperview() })
  4909. addSubviewMultipleSession()
  4910. return
  4911. }
  4912. let message = dataMessages[indexPath.row]
  4913. if let attachmentFlag = message["attachment_flag"], let attachmentFlag = attachmentFlag as? String {
  4914. if attachmentFlag == "27" || attachmentFlag == "26" {
  4915. let streamingController = (attachmentFlag == "27") ? QmeraCreateStreamingViewController() : CreateSeminarViewController()
  4916. if let messageText = message["message_text"],
  4917. let messageText = messageText as? String,
  4918. var json = try! JSONSerialization.jsonObject(with: messageText.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
  4919. if json["blog"] == nil {
  4920. json["blog"] = message["blog_id"] ?? nil
  4921. }
  4922. switch(attachmentFlag){
  4923. case "27":
  4924. (streamingController as! QmeraCreateStreamingViewController).data = json
  4925. default:
  4926. (streamingController as! CreateSeminarViewController).data = json
  4927. }
  4928. if json["by"] as? String != User.getMyPin() as String? {
  4929. switch(attachmentFlag){
  4930. case "27":
  4931. (streamingController as! QmeraCreateStreamingViewController).isJoin = true
  4932. default:
  4933. (streamingController as! CreateSeminarViewController).isJoin = true
  4934. }
  4935. }
  4936. }
  4937. let streamingNav = CustomNavigationController(rootViewController: streamingController)
  4938. streamingNav.modalPresentationStyle = .custom
  4939. streamingNav.navigationBar.tintColor = .white
  4940. streamingNav.navigationBar.barTintColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : .mainColor
  4941. streamingNav.navigationBar.isTranslucent = false
  4942. streamingNav.navigationBar.overrideUserInterfaceStyle = .dark
  4943. streamingNav.navigationBar.barStyle = .black
  4944. let cancelButtonAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.font : UIFont.systemFont(ofSize: 16)]
  4945. UIBarButtonItem.appearance().setTitleTextAttributes(cancelButtonAttributes, for: .normal)
  4946. let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
  4947. streamingNav.navigationBar.titleTextAttributes = textAttributes
  4948. streamingNav.view.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : .mainColor
  4949. streamingNav.navigationBar.isTranslucent = false
  4950. navigationController?.present(streamingNav, animated: true, completion: nil)
  4951. } else if message["message_scope_id"] as? String == "18" {
  4952. let formView = FormEditor()
  4953. let messageText = message["message_text"] as? String ?? ""
  4954. formView.jsonData = messageText
  4955. formView.dataMessage = message
  4956. formView.dataPerson = self.dataPerson
  4957. formView.modalPresentationStyle = .custom
  4958. formView.modalTransitionStyle = .crossDissolve
  4959. formView.view.backgroundColor = .black.withAlphaComponent(0.2)
  4960. self.present(formView, animated: true, completion: nil)
  4961. }
  4962. }
  4963. }
  4964. public func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
  4965. let containerView = UIView()
  4966. containerView.backgroundColor = .clear
  4967. let dateView = UIView()
  4968. containerView.addSubview(dateView)
  4969. dateView.translatesAutoresizingMaskIntoConstraints = false
  4970. var topAnchor = dateView.topAnchor.constraint(equalTo: containerView.topAnchor)
  4971. topAnchor = dateView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 10.0)
  4972. NSLayoutConstraint.activate([
  4973. topAnchor,
  4974. dateView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
  4975. dateView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor),
  4976. dateView.heightAnchor.constraint(equalToConstant: 30),
  4977. dateView.widthAnchor.constraint(greaterThanOrEqualToConstant: 60)
  4978. ])
  4979. dateView.backgroundColor = .orangeColor
  4980. dateView.layer.cornerRadius = 15.0
  4981. dateView.clipsToBounds = true
  4982. let labelDate = UILabel()
  4983. dateView.addSubview(labelDate)
  4984. labelDate.translatesAutoresizingMaskIntoConstraints = false
  4985. NSLayoutConstraint.activate([
  4986. labelDate.centerYAnchor.constraint(equalTo: dateView.centerYAnchor),
  4987. labelDate.centerXAnchor.constraint(equalTo: dateView.centerXAnchor),
  4988. labelDate.leadingAnchor.constraint(equalTo: dateView.leadingAnchor, constant: 10),
  4989. labelDate.trailingAnchor.constraint(equalTo: dateView.trailingAnchor, constant: -10),
  4990. ])
  4991. labelDate.textAlignment = .center
  4992. labelDate.textColor = .secondaryColor
  4993. labelDate.font = UIFont.systemFont(ofSize: 12, weight: .medium)
  4994. labelDate.text = dataDates[section]
  4995. if listViewOnSection.count == 0 || listViewOnSection.count - 1 < section {
  4996. listViewOnSection.append(containerView)
  4997. } else {
  4998. listViewOnSection.remove(at: section)
  4999. listViewOnSection.insert(containerView, at: section)
  5000. }
  5001. return containerView
  5002. }
  5003. public func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
  5004. return 40
  5005. }
  5006. public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  5007. let idMe = User.getMyPin() as String?
  5008. let dataMessages = dataMessages.filter({$0["chat_date"] as? String ?? "" == dataDates[indexPath.section]})
  5009. let profileMessage = UIImageView()
  5010. let cell = tableView.dequeueReusableCell(withIdentifier: "cellEditorPersonal", for: indexPath as IndexPath)
  5011. cell.contentView.subviews.forEach({ $0.removeConstraints($0.constraints) })
  5012. cell.contentView.subviews.forEach({ $0.removeFromSuperview() })
  5013. if isContactCenter && isRequestContactCenter && dataMessages[indexPath.row]["category_cc"] != nil {
  5014. cell.backgroundColor = .clear
  5015. cell.selectionStyle = .none
  5016. if dataMessages[indexPath.row]["category_cc"] is [CategoryCC] {
  5017. let category_cc = dataMessages[indexPath.row]["category_cc"] as! [CategoryCC]
  5018. profileMessage.frame.size = CGSize(width: 35, height: 35)
  5019. cell.contentView.addSubview(profileMessage)
  5020. profileMessage.translatesAutoresizingMaskIntoConstraints = false
  5021. profileMessage.topAnchor.constraint(equalTo: cell.contentView.topAnchor, constant: 5).isActive = true
  5022. profileMessage.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: 15).isActive = true
  5023. profileMessage.heightAnchor.constraint(equalToConstant: 37).isActive = true
  5024. profileMessage.widthAnchor.constraint(equalToConstant: 35).isActive = true
  5025. profileMessage.circle()
  5026. profileMessage.clipsToBounds = true
  5027. profileMessage.backgroundColor = .lightGray
  5028. profileMessage.image = UIImage(systemName: "person")
  5029. profileMessage.tintColor = .white
  5030. profileMessage.contentMode = .scaleAspectFit
  5031. getImage(name: dataPerson["picture"]!!, placeholderImage: UIImage(systemName: "person.circle.fill")!) { result, isDownloaded, image in
  5032. profileMessage.image = image
  5033. }
  5034. profileMessage.contentMode = .scaleAspectFill
  5035. let containerMessage = UIView()
  5036. cell.contentView.addSubview(containerMessage)
  5037. containerMessage.translatesAutoresizingMaskIntoConstraints = false
  5038. containerMessage.topAnchor.constraint(equalTo: profileMessage.bottomAnchor).isActive = true
  5039. containerMessage.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: 5).isActive = true
  5040. containerMessage.trailingAnchor.constraint(lessThanOrEqualTo: cell.contentView.trailingAnchor, constant: -60).isActive = true
  5041. containerMessage.widthAnchor.constraint(greaterThanOrEqualToConstant: 46).isActive = true
  5042. // containerMessage.backgroundColor = .grayColor
  5043. // containerMessage.layer.cornerRadius = 10.0
  5044. // containerMessage.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMinYCorner, .layerMaxXMaxYCorner]
  5045. // containerMessage.clipsToBounds = true
  5046. // let timeMessage = UILabel()
  5047. // cell.contentView.addSubview(timeMessage)
  5048. // timeMessage.translatesAutoresizingMaskIntoConstraints = false
  5049. // timeMessage.leadingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: 8).isActive = true
  5050. let messageText = UILabel()
  5051. containerMessage.addSubview(messageText)
  5052. messageText.translatesAutoresizingMaskIntoConstraints = false
  5053. messageText.numberOfLines = 0
  5054. messageText.lineBreakMode = .byWordWrapping
  5055. containerMessage.addSubview(messageText)
  5056. messageText.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 5).isActive = true
  5057. messageText.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
  5058. messageText.bottomAnchor.constraint(equalTo: containerMessage.bottomAnchor, constant: -5).isActive = true
  5059. messageText.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  5060. if category_cc[0].id.contains("level0_") || dataMessages[indexPath.row]["attachment_flag"] != nil && dataMessages[indexPath.row]["attachment_flag"] as? String ?? "" == "503" {
  5061. messageText.text = "Welcome to".localized() + " " + dataPerson["name"]!! + " " + "Contact Center".localized()
  5062. + "\n" + "Please choose your desired communication method...".localized()
  5063. } else if category_cc[0].id.contains("level1_") {
  5064. messageText.text = "Please select your Consultation Topic:".localized()
  5065. } else if !category_cc[0].id.contains("level1_") && dataMessages[indexPath.row]["attachment_flag"] == nil {
  5066. messageText.text = "Please select the type of topic that you chosen".localized()
  5067. } else if dataMessages[indexPath.row]["attachment_flag"] != nil && dataMessages[indexPath.row]["attachment_flag"] as? String ?? "" == "502" {
  5068. messageText.text = "Please select the information option:".localized()
  5069. } else {
  5070. messageText.text = "Sorry, currently all our representatives are busy helping other customers. Do you want us to get back to you as soon as one of them is available?".localized()
  5071. }
  5072. messageText.font = UIFont.systemFont(ofSize: 14, weight: .medium)
  5073. messageText.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
  5074. // let date = Date()
  5075. // let formatter = DateFormatter()
  5076. // formatter.dateFormat = "HH:mm"
  5077. // formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
  5078. // timeMessage.text = formatter.string(from: date as Date)
  5079. // timeMessage.font = UIFont.systemFont(ofSize: 10, weight: .medium)
  5080. // timeMessage.textColor = .lightGray
  5081. let containerButton = UIView()
  5082. cell.contentView.addSubview(containerButton)
  5083. containerButton.translatesAutoresizingMaskIntoConstraints = false
  5084. containerButton.topAnchor.constraint(equalTo: containerMessage.bottomAnchor, constant: 5).isActive = true
  5085. containerButton.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor, constant: -5).isActive = true
  5086. containerButton.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: 15).isActive = true
  5087. containerButton.widthAnchor.constraint(equalToConstant: self.view!.frame.size.width * 0.9).isActive = true
  5088. containerButton.heightAnchor.constraint(greaterThanOrEqualToConstant: 55).isActive = true
  5089. containerButton.backgroundColor = .clear
  5090. // timeMessage.bottomAnchor.constraint(equalTo:containerButton.topAnchor, constant: -5).isActive = true
  5091. for i in 0..<category_cc.count {
  5092. let buttonChat = UIButton(type: .custom)
  5093. containerButton.addSubview(buttonChat)
  5094. buttonChat.translatesAutoresizingMaskIntoConstraints = false
  5095. buttonChat.widthAnchor.constraint(equalToConstant: self.view!.frame.size.width * 0.9 / 2 - 5).isActive = true
  5096. buttonChat.heightAnchor.constraint(greaterThanOrEqualToConstant: 55).isActive = true
  5097. if i % 2 == 0 {
  5098. if i / 2 + 1 == 1 {
  5099. buttonChat.topAnchor.constraint(equalTo: containerButton.topAnchor, constant: 5).isActive = true
  5100. } else {
  5101. var constantTop = (i / 2 - 1) * 50
  5102. if constantTop == 0 {
  5103. constantTop = 55
  5104. } else {
  5105. constantTop = constantTop + 55
  5106. }
  5107. buttonChat.topAnchor.constraint(equalTo: containerButton.topAnchor, constant: CGFloat(constantTop)).isActive = true
  5108. }
  5109. if i == category_cc.count - 1 {
  5110. buttonChat.bottomAnchor.constraint(equalTo: containerButton.bottomAnchor, constant: -5).isActive = true
  5111. }
  5112. buttonChat.leadingAnchor.constraint(equalTo: containerButton.leadingAnchor, constant: 5).isActive = true
  5113. } else {
  5114. let newi = i - 1
  5115. if newi / 2 + 1 == 1 {
  5116. buttonChat.topAnchor.constraint(equalTo: containerButton.topAnchor, constant: 5).isActive = true
  5117. } else {
  5118. var constantTop = (newi / 2 - 1) * 50
  5119. if constantTop == 0 {
  5120. constantTop = 55
  5121. } else {
  5122. constantTop = constantTop + 55
  5123. }
  5124. buttonChat.topAnchor.constraint(equalTo: containerButton.topAnchor, constant: CGFloat(constantTop)).isActive = true
  5125. }
  5126. if i == category_cc.count - 1 {
  5127. buttonChat.bottomAnchor.constraint(equalTo: containerButton.bottomAnchor, constant: -5).isActive = true
  5128. }
  5129. buttonChat.trailingAnchor.constraint(equalTo: containerButton.trailingAnchor, constant: -5).isActive = true
  5130. }
  5131. if category_cc[i].isActive {
  5132. buttonChat.backgroundColor = .orangeBNI
  5133. }
  5134. var nameImage = "pb_cc_bg_messaging"
  5135. if i == 1 {
  5136. nameImage = "pb_cc_bg_sms"
  5137. } else if i == 2 {
  5138. nameImage = "pb_cc_bg_voip"
  5139. } else if i == 3 {
  5140. nameImage = "pb_cc_bg_email"
  5141. } else if i == 4 {
  5142. nameImage = "pb_cc_bg_videocall"
  5143. } else if i == 5 {
  5144. nameImage = "pb_cc_bg_gsmcall"
  5145. } else if i == 6 {
  5146. nameImage = "pb_cc_bg_gptchatbot"
  5147. } else if i == 7 {
  5148. nameImage = "pb_cc_bg_whatsapp"
  5149. }
  5150. buttonChat.setImage(resizeImage(image: UIImage(named: nameImage, in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, targetSize: CGSize(width: self.view!.frame.size.width * 0.9 / 2 - 5, height: 55)), for: .normal)
  5151. // buttonChat.setTitle(category_cc[i].service_name.localized(), for: .normal)
  5152. // buttonChat.setTitleColor(.black, for: .normal)
  5153. // buttonChat.setImage(resizeImage(image: UIImage(named: "pb_gpt_bot", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, targetSize: CGSize(width: 30, height: 30)), for: .normal)
  5154. // buttonChat.contentHorizontalAlignment = .left
  5155. // buttonChat.imageEdgeInsets = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 0) // Adjust left inset for the image
  5156. // buttonChat.titleEdgeInsets = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 0) // Adjust left inset for the title
  5157. // buttonChat.titleLabel?.font = UIFont.boldSystemFont(ofSize: 12)
  5158. // buttonChat.titleLabel?.numberOfLines = 0
  5159. // buttonChat.layer.borderWidth = 2
  5160. // buttonChat.layer.borderColor = UIColor.white.cgColor
  5161. // buttonChat.backgroundColor = .grayColor
  5162. // buttonChat.layer.cornerRadius = 8.0
  5163. // buttonChat.clipsToBounds = true
  5164. buttonChat.restorationIdentifier = "\(category_cc[i].id),\(category_cc[i].service_id)"
  5165. if dataMessages[indexPath.row]["attachment_flag"] != nil {
  5166. buttonChat.tag = Int(dataMessages[indexPath.row]["attachment_flag"] as? String ?? "")!
  5167. }
  5168. buttonChat.addTarget(self, action: #selector(ccAction(sender:)), for: .touchUpInside)
  5169. }
  5170. } else {
  5171. let messageWait = UILabel()
  5172. cell.contentView.addSubview(messageWait)
  5173. messageWait.translatesAutoresizingMaskIntoConstraints = false
  5174. messageWait.topAnchor.constraint(equalTo: cell.contentView.topAnchor).isActive = true
  5175. messageWait.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor).isActive = true
  5176. messageWait.leftAnchor.constraint(equalTo: cell.contentView.leftAnchor, constant: 10).isActive = true
  5177. messageWait.rightAnchor.constraint(equalTo: cell.contentView.rightAnchor, constant: -10).isActive = true
  5178. messageWait.text = dataMessages[indexPath.row]["category_cc"] as? String ?? dataMessages[indexPath.row]["message_text"] as? String ?? ""
  5179. messageWait.numberOfLines = 0
  5180. messageWait.font = UIFont.systemFont(ofSize: 12)
  5181. messageWait.textColor = .gray
  5182. messageWait.textAlignment = .center
  5183. }
  5184. return cell
  5185. }
  5186. let messageIdChat = (dataMessages[indexPath.row]["message_id"] as? String) ?? ""
  5187. let thumbChat = (dataMessages[indexPath.row]["thumb_id"] as? String) ?? ""
  5188. let imageChat = (dataMessages[indexPath.row]["image_id"] as? String) ?? ""
  5189. let videoChat = (dataMessages[indexPath.row]["video_id"] as? String) ?? ""
  5190. let fileChat = (dataMessages[indexPath.row]["file_id"] as? String) ?? ""
  5191. let reffChat = (dataMessages[indexPath.row]["reff_id"] as? String) ?? ""
  5192. let audioChat = (dataMessages[indexPath.row]["audio_id"] as? String) ?? ""
  5193. let gifChat = (dataMessages[indexPath.row]["gif_id"] as? String) ?? ""
  5194. let dataTimer = listTimerCredential[(dataMessages[indexPath.row]["message_id"] as? String ?? "")]
  5195. cell.backgroundColor = .clear
  5196. cell.selectionStyle = .none
  5197. let nameSender = UILabel()
  5198. if isContactCenter {
  5199. profileMessage.frame.size = CGSize(width: 35, height: 35)
  5200. cell.contentView.addSubview(profileMessage)
  5201. profileMessage.translatesAutoresizingMaskIntoConstraints = false
  5202. profileMessage.topAnchor.constraint(equalTo: cell.contentView.topAnchor, constant: 5).isActive = true
  5203. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  5204. profileMessage.trailingAnchor.constraint(equalTo: cell.contentView.trailingAnchor, constant: -15).isActive = true
  5205. } else {
  5206. if copySession || forwardSession || deleteSession {
  5207. profileMessage.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: 50).isActive = true
  5208. } else {
  5209. profileMessage.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: 15).isActive = true
  5210. }
  5211. }
  5212. profileMessage.heightAnchor.constraint(equalToConstant: 37).isActive = true
  5213. profileMessage.widthAnchor.constraint(equalToConstant: 35).isActive = true
  5214. profileMessage.circle()
  5215. profileMessage.clipsToBounds = true
  5216. profileMessage.backgroundColor = .lightGray
  5217. profileMessage.image = UIImage(systemName: "person")
  5218. profileMessage.tintColor = .white
  5219. profileMessage.contentMode = .scaleAspectFit
  5220. let user = User.getData(pin: dataMessages[indexPath.row]["f_pin"] as? String)
  5221. getImage(name: user?.thumb ?? "", placeholderImage: UIImage(systemName: "person.circle.fill")!, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
  5222. profileMessage.image = image
  5223. }
  5224. profileMessage.contentMode = .scaleAspectFill
  5225. cell.contentView.addSubview(nameSender)
  5226. nameSender.translatesAutoresizingMaskIntoConstraints = false
  5227. if markerCounter != nil && dataMessages[indexPath.row]["message_id"] as? String == markerCounter {
  5228. nameSender.topAnchor.constraint(equalTo: cell.contentView.topAnchor, constant: 35).isActive = true
  5229. } else {
  5230. nameSender.topAnchor.constraint(equalTo: cell.contentView.topAnchor, constant: 5).isActive = true
  5231. }
  5232. nameSender.font = UIFont.systemFont(ofSize: 12, weight: UIFont.Weight(800))
  5233. nameSender.text = user?.fullName ?? ""
  5234. nameSender.textAlignment = .right
  5235. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  5236. nameSender.trailingAnchor.constraint(equalTo:profileMessage.leadingAnchor, constant: -5).isActive = true
  5237. nameSender.textColor = .systemBlue
  5238. } else {
  5239. nameSender.leadingAnchor.constraint(equalTo:profileMessage.trailingAnchor, constant: 5).isActive = true
  5240. nameSender.textColor = .orangeColor
  5241. }
  5242. }
  5243. let containerMessage = UIView()
  5244. cell.contentView.addSubview(containerMessage)
  5245. containerMessage.translatesAutoresizingMaskIntoConstraints = false
  5246. let timeMessage = UILabel()
  5247. cell.contentView.addSubview(timeMessage)
  5248. timeMessage.translatesAutoresizingMaskIntoConstraints = false
  5249. if (dataMessages[indexPath.row]["read_receipts"] as? String) == "8" || ((dataMessages[indexPath.row]["credential"] as? String) == "1" && dataMessages[indexPath.row]["lock"] as? String != "2") {
  5250. timeMessage.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor, constant: -40).isActive = true
  5251. } else {
  5252. timeMessage.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor, constant: -5).isActive = true
  5253. }
  5254. let statusMessage = UIImageView()
  5255. if (dataMessages[indexPath.row]["attachment_flag"] as? String == "0" && dataMessages[indexPath.row]["lock"] as? String != "1") || forwardSession || deleteSession {
  5256. var showSelectedImage = true
  5257. if (!imageChat.isEmpty || !videoChat.isEmpty || !fileChat.isEmpty) && forwardSession {
  5258. var file = dataMessages[indexPath.row]["image_id"] as? String ?? ""
  5259. if file.isEmpty {
  5260. file = dataMessages[indexPath.row]["video_id"] as? String ?? ""
  5261. if file.isEmpty {
  5262. file = dataMessages[indexPath.row]["file_id"] as? String ?? ""
  5263. }
  5264. }
  5265. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  5266. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  5267. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  5268. if let dirPath = paths.first {
  5269. let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(file)
  5270. if !FileManager.default.fileExists(atPath: fileURL.path) && !FileEncryption.shared.isSecureExists(filename: fileURL.lastPathComponent) {
  5271. showSelectedImage = false
  5272. }
  5273. }
  5274. }
  5275. if showSelectedImage {
  5276. let selectedImage = UIImageView()
  5277. cell.contentView.addSubview(selectedImage)
  5278. selectedImage.translatesAutoresizingMaskIntoConstraints = false
  5279. selectedImage.frame.size = CGSize(width: 20, height: 20)
  5280. var leading = selectedImage.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: -20)
  5281. selectedImage.isHidden = true
  5282. if copySession || forwardSession || deleteSession {
  5283. leading = selectedImage.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: 15)
  5284. selectedImage.isHidden = false
  5285. }
  5286. NSLayoutConstraint.activate([
  5287. leading,
  5288. selectedImage.centerYAnchor.constraint(equalTo: cell.contentView.centerYAnchor),
  5289. selectedImage.widthAnchor.constraint(equalToConstant: 20),
  5290. selectedImage.heightAnchor.constraint(equalToConstant: 20)
  5291. ])
  5292. selectedImage.circle()
  5293. selectedImage.layer.borderWidth = 2
  5294. selectedImage.layer.borderColor = UIColor.mainColor.cgColor
  5295. if dataMessages[indexPath.row]["isSelected"] as! Bool {
  5296. selectedImage.image = UIImage(systemName: "checkmark.circle.fill")
  5297. }
  5298. selectedImage.tintColor = .mainColor
  5299. }
  5300. }
  5301. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  5302. containerMessage.leadingAnchor.constraint(greaterThanOrEqualTo: cell.contentView.leadingAnchor, constant: 60).isActive = true
  5303. if (dataMessages[indexPath.row]["read_receipts"] as? String) == "8" || ((dataMessages[indexPath.row]["credential"] as? String) == "1" && dataMessages[indexPath.row]["lock"] as? String != "2") {
  5304. containerMessage.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor, constant: -40).isActive = true
  5305. } else {
  5306. containerMessage.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor, constant: -5).isActive = true
  5307. }
  5308. if isContactCenter {
  5309. containerMessage.topAnchor.constraint(equalTo: nameSender.bottomAnchor).isActive = true
  5310. containerMessage.trailingAnchor.constraint(equalTo: profileMessage.leadingAnchor, constant: -5).isActive = true
  5311. } else {
  5312. containerMessage.topAnchor.constraint(equalTo: cell.contentView.topAnchor, constant: 5).isActive = true
  5313. containerMessage.trailingAnchor.constraint(equalTo: cell.contentView.trailingAnchor, constant: -15).isActive = true
  5314. }
  5315. containerMessage.widthAnchor.constraint(greaterThanOrEqualToConstant: 46).isActive = true
  5316. if (dataMessages[indexPath.row]["attachment_flag"] as? String == "11" && dataMessages[indexPath.row]["reff_id"]as? String == "") {
  5317. containerMessage.backgroundColor = .clear
  5318. } else {
  5319. containerMessage.backgroundColor = .blueBubbleColor
  5320. }
  5321. containerMessage.layer.cornerRadius = 10.0
  5322. containerMessage.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner, .layerMinXMinYCorner]
  5323. containerMessage.clipsToBounds = true
  5324. timeMessage.trailingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: -8).isActive = true
  5325. if (dataMessages[indexPath.row]["lock"] as? String == "0" || (dataMessages[indexPath.row]["lock"] as? String ?? "").isEmpty) {
  5326. cell.contentView.addSubview(statusMessage)
  5327. statusMessage.translatesAutoresizingMaskIntoConstraints = false
  5328. statusMessage.bottomAnchor.constraint(equalTo: timeMessage.topAnchor).isActive = true
  5329. statusMessage.trailingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: -8).isActive = true
  5330. statusMessage.widthAnchor.constraint(equalToConstant: 15).isActive = true
  5331. statusMessage.heightAnchor.constraint(equalToConstant: 15).isActive = true
  5332. if dataMessages[indexPath.row]["status"]! as? String ?? "" == "0" {
  5333. statusMessage.image = UIImage(systemName: "xmark.circle")!.withTintColor(UIColor.red, renderingMode: .alwaysOriginal)
  5334. } else if dataMessages[indexPath.row]["status"]! as? String ?? "" == "1" {
  5335. statusMessage.image = UIImage(systemName: "clock.arrow.circlepath")!.withTintColor(UIColor.lightGray, renderingMode: .alwaysOriginal)
  5336. } else if (dataMessages[indexPath.row]["status"]! as? String ?? "" == "2" ) {
  5337. statusMessage.image = UIImage(named: "checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.lightGray)
  5338. } else if (dataMessages[indexPath.row]["status"]! as? String ?? "" == "3") {
  5339. statusMessage.image = UIImage(named: "double-checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.lightGray)
  5340. } else if (dataMessages[indexPath.row]["status"]! as? String ?? "" == "8") {
  5341. statusMessage.image = UIImage(named: "message_status_ack", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withRenderingMode(.alwaysOriginal)
  5342. } else {
  5343. statusMessage.image = UIImage(named: "double-checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.systemBlue)
  5344. }
  5345. }
  5346. } else {
  5347. if markerCounter != nil && dataMessages[indexPath.row]["message_id"] as? String == markerCounter {
  5348. if isContactCenter {
  5349. containerMessage.topAnchor.constraint(equalTo: nameSender.bottomAnchor).isActive = true
  5350. } else {
  5351. containerMessage.topAnchor.constraint(equalTo: cell.contentView.topAnchor, constant: 35).isActive = true
  5352. }
  5353. let newMessagesView = UIView()
  5354. cell.contentView.addSubview(newMessagesView)
  5355. newMessagesView.translatesAutoresizingMaskIntoConstraints = false
  5356. NSLayoutConstraint.activate([
  5357. newMessagesView.topAnchor.constraint(equalTo: newMessagesView.topAnchor),
  5358. newMessagesView.bottomAnchor.constraint(equalTo: containerMessage.topAnchor),
  5359. newMessagesView.centerXAnchor.constraint(equalTo: cell.contentView.centerXAnchor),
  5360. newMessagesView.heightAnchor.constraint(equalToConstant: 30),
  5361. newMessagesView.widthAnchor.constraint(greaterThanOrEqualToConstant: 60)
  5362. ])
  5363. newMessagesView.backgroundColor = .greenColor
  5364. newMessagesView.layer.cornerRadius = 15.0
  5365. newMessagesView.clipsToBounds = true
  5366. let labelNewMessages = UILabel()
  5367. newMessagesView.addSubview(labelNewMessages)
  5368. labelNewMessages.translatesAutoresizingMaskIntoConstraints = false
  5369. NSLayoutConstraint.activate([
  5370. labelNewMessages.centerYAnchor.constraint(equalTo: newMessagesView.centerYAnchor),
  5371. labelNewMessages.centerXAnchor.constraint(equalTo: newMessagesView.centerXAnchor),
  5372. labelNewMessages.leadingAnchor.constraint(equalTo: newMessagesView.leadingAnchor, constant: 10),
  5373. labelNewMessages.trailingAnchor.constraint(equalTo: newMessagesView.trailingAnchor, constant: -10),
  5374. ])
  5375. labelNewMessages.textAlignment = .center
  5376. labelNewMessages.textColor = .secondaryColor
  5377. labelNewMessages.font = UIFont.systemFont(ofSize: 12, weight: .medium)
  5378. labelNewMessages.text = "Unread Messages".localized()
  5379. } else {
  5380. if isContactCenter {
  5381. containerMessage.topAnchor.constraint(equalTo: nameSender.bottomAnchor).isActive = true
  5382. } else {
  5383. containerMessage.topAnchor.constraint(equalTo: cell.contentView.topAnchor, constant: 5).isActive = true
  5384. }
  5385. }
  5386. if isContactCenter {
  5387. containerMessage.leadingAnchor.constraint(equalTo: profileMessage.trailingAnchor, constant: 5).isActive = true
  5388. } else {
  5389. if copySession || forwardSession || deleteSession {
  5390. containerMessage.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: 50).isActive = true
  5391. } else {
  5392. containerMessage.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: 15).isActive = true
  5393. }
  5394. }
  5395. if (dataMessages[indexPath.row]["read_receipts"] as? String) == "8" || ((dataMessages[indexPath.row]["credential"] as? String) == "1" && dataMessages[indexPath.row]["lock"] as? String != "2") {
  5396. containerMessage.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor, constant: -40).isActive = true
  5397. } else {
  5398. containerMessage.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor, constant: -5).isActive = true
  5399. }
  5400. containerMessage.trailingAnchor.constraint(lessThanOrEqualTo: cell.contentView.trailingAnchor, constant: -60).isActive = true
  5401. containerMessage.widthAnchor.constraint(greaterThanOrEqualToConstant: 46).isActive = true
  5402. if dataMessages[indexPath.row]["attachment_flag"] as? String == "11" && dataMessages[indexPath.row]["reff_id"]as? String == "" && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as? String ?? "" != "1") {
  5403. containerMessage.backgroundColor = .clear
  5404. } else {
  5405. containerMessage.backgroundColor = .whiteBubbleColor
  5406. }
  5407. containerMessage.layer.cornerRadius = 10.0
  5408. containerMessage.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMinYCorner, .layerMaxXMaxYCorner]
  5409. containerMessage.clipsToBounds = true
  5410. timeMessage.leadingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: 8).isActive = true
  5411. }
  5412. let imageStared = UIImageView()
  5413. if dataMessages[indexPath.row]["is_stared"] as? String == "1" && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as? String ?? "" == "0") {
  5414. cell.contentView.addSubview(imageStared)
  5415. imageStared.translatesAutoresizingMaskIntoConstraints = false
  5416. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  5417. imageStared.bottomAnchor.constraint(equalTo: statusMessage.topAnchor).isActive = true
  5418. imageStared.trailingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: -8).isActive = true
  5419. } else {
  5420. imageStared.bottomAnchor.constraint(equalTo: timeMessage.topAnchor).isActive = true
  5421. imageStared.leadingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: 8).isActive = true
  5422. }
  5423. imageStared.widthAnchor.constraint(equalToConstant: 15).isActive = true
  5424. imageStared.heightAnchor.constraint(equalToConstant: 15).isActive = true
  5425. imageStared.image = UIImage(systemName: "star.fill")
  5426. imageStared.backgroundColor = .clear
  5427. imageStared.tintColor = .systemYellow
  5428. }
  5429. if dataMessages[indexPath.row]["read_receipts"] as? String == "8" {
  5430. let imageAckView = UIImageView()
  5431. var imageAck = UIImage(named: "ack_icon_gray", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withRenderingMode(.alwaysOriginal)
  5432. if dataMessages[indexPath.row]["status"] as? String == "8" {
  5433. imageAck = UIImage(named: "ack_icon", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withRenderingMode(.alwaysOriginal)
  5434. }
  5435. imageAckView.image = imageAck
  5436. cell.contentView.addSubview(imageAckView)
  5437. imageAckView.translatesAutoresizingMaskIntoConstraints = false
  5438. imageAckView.widthAnchor.constraint(equalToConstant: 30).isActive = true
  5439. imageAckView.heightAnchor.constraint(equalToConstant: 30).isActive = true
  5440. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  5441. imageAckView.topAnchor.constraint(equalTo: containerMessage.bottomAnchor, constant: 5).isActive = true
  5442. imageAckView.trailingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 30).isActive = true
  5443. } else {
  5444. imageAckView.topAnchor.constraint(equalTo: containerMessage.bottomAnchor, constant: 5).isActive = true
  5445. imageAckView.leadingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -30).isActive = true
  5446. let tap = ObjectGesture(target: self, action: #selector(tapAck(_:)))
  5447. tap.indexPath = indexPath
  5448. imageAckView.addGestureRecognizer(tap)
  5449. imageAckView.isUserInteractionEnabled = true
  5450. }
  5451. }
  5452. if (dataMessages[indexPath.row]["credential"] as? String) == "1" && (dataMessages[indexPath.row]["lock"] as? String) != "2" {
  5453. let imageCredentialView = UIImageView()
  5454. let imageCredential = UIImage(named: "confidential_icon", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withRenderingMode(.alwaysOriginal)
  5455. imageCredentialView.image = imageCredential
  5456. cell.contentView.addSubview(imageCredentialView)
  5457. imageCredentialView.translatesAutoresizingMaskIntoConstraints = false
  5458. imageCredentialView.widthAnchor.constraint(equalToConstant: 30).isActive = true
  5459. imageCredentialView.heightAnchor.constraint(equalToConstant: 30).isActive = true
  5460. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  5461. imageCredentialView.topAnchor.constraint(equalTo: containerMessage.bottomAnchor, constant: 5).isActive = true
  5462. imageCredentialView.trailingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 30).isActive = true
  5463. } else {
  5464. imageCredentialView.topAnchor.constraint(equalTo: containerMessage.bottomAnchor, constant: 5).isActive = true
  5465. imageCredentialView.leadingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -30).isActive = true
  5466. }
  5467. }
  5468. if dataMessages[indexPath.row][TypeDataMessage.last_edit] != nil && dataMessages[indexPath.row][TypeDataMessage.last_edit] as! Int64 != 0 {
  5469. let editedText = UILabel()
  5470. editedText.text = "Edited".localized()
  5471. editedText.font = UIFont.systemFont(ofSize: 10, weight: .medium)
  5472. editedText.textColor = .lightGray
  5473. cell.contentView.addSubview(editedText)
  5474. editedText.translatesAutoresizingMaskIntoConstraints = false
  5475. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  5476. editedText.trailingAnchor.constraint(equalTo: timeMessage.leadingAnchor, constant: -2).isActive = true
  5477. } else {
  5478. editedText.leadingAnchor.constraint(equalTo: timeMessage.trailingAnchor, constant: 2).isActive = true
  5479. }
  5480. editedText.bottomAnchor.constraint(equalTo: containerMessage.bottomAnchor).isActive = true
  5481. }
  5482. let messageText = UITextView()
  5483. messageText.isEditable = false
  5484. messageText.isSelectable = true
  5485. messageText.dataDetectorTypes = []
  5486. messageText.backgroundColor = .clear
  5487. messageText.isScrollEnabled = false
  5488. messageText.textContainerInset = UIEdgeInsets.zero
  5489. messageText.contentInset = UIEdgeInsets.zero
  5490. messageText.textDragInteraction?.isEnabled = false
  5491. containerMessage.addSubview(messageText)
  5492. messageText.translatesAutoresizingMaskIntoConstraints = false
  5493. let topMarginText = messageText.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 15)
  5494. messageText.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
  5495. messageText.font = .systemFont(ofSize: 12)
  5496. if dataMessages[indexPath.row]["attachment_flag"] as? String == "27" || dataMessages[indexPath.row]["attachment_flag"] as? String == "26" || dataMessages[indexPath.row]["message_scope_id"] as? String == "18" {
  5497. messageText.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 85).isActive = true
  5498. let imageLS = UIImageView()
  5499. containerMessage.addSubview(imageLS)
  5500. imageLS.translatesAutoresizingMaskIntoConstraints = false
  5501. NSLayoutConstraint.activate([
  5502. imageLS.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15.0),
  5503. imageLS.trailingAnchor.constraint(equalTo: messageText.leadingAnchor, constant: -10.0),
  5504. imageLS.centerYAnchor.constraint(equalTo: containerMessage.centerYAnchor),
  5505. imageLS.heightAnchor.constraint(equalToConstant: 60.0)
  5506. ])
  5507. if dataMessages[indexPath.row]["attachment_flag"] as? String ?? "" == "26" {
  5508. imageLS.image = UIImage(named: "pb_seminar_wpr", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  5509. } else if dataMessages[indexPath.row]["attachment_flag"] as? String ?? "" == "27" {
  5510. imageLS.image = UIImage(named: "pb_live_tv", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  5511. } else if dataMessages[indexPath.row]["message_scope_id"] as? String == "18" {
  5512. imageLS.image = UIImage(systemName: "doc.richtext.fill")
  5513. imageLS.tintColor = .mainColor
  5514. }
  5515. } else if !audioChat.isEmpty {
  5516. messageText.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 60).isActive = true
  5517. } else {
  5518. messageText.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
  5519. }
  5520. if dataMessages[indexPath.row]["f_pin"] as? String == "-999" && (dataMessages[indexPath.row]["blog_id"] as? String) != nil && !(dataMessages[indexPath.row]["blog_id"] as? String ?? "").isEmpty && (dataMessages[indexPath.row]["message_text"] as? String ?? "").contains("Berikut QR Code dan detil booking Anda") {
  5521. messageText.bottomAnchor.constraint(equalTo: containerMessage.bottomAnchor, constant: -115).isActive = true
  5522. let imageQR = UIImageView()
  5523. containerMessage.addSubview(imageQR)
  5524. imageQR.translatesAutoresizingMaskIntoConstraints = false
  5525. NSLayoutConstraint.activate([
  5526. imageQR.centerXAnchor.constraint(equalTo: containerMessage.centerXAnchor),
  5527. imageQR.topAnchor.constraint(equalTo: messageText.bottomAnchor),
  5528. imageQR.widthAnchor.constraint(equalToConstant: 100.0),
  5529. imageQR.heightAnchor.constraint(equalToConstant: 100.0)
  5530. ])
  5531. imageQR.image = generateQRCode(from: dataMessages[indexPath.row]["blog_id"] as? String ?? "")
  5532. } else {
  5533. messageText.bottomAnchor.constraint(equalTo: containerMessage.bottomAnchor, constant: -15).isActive = true
  5534. }
  5535. messageText.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  5536. var textChat = (dataMessages[indexPath.row]["message_text"] as? String) ?? ""
  5537. let originalMessageText = textChat
  5538. if (dataMessages[indexPath.row]["lock"] != nil && (dataMessages[indexPath.row]["lock"])! as? String == "1") {
  5539. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  5540. textChat = "🚫 _"+"You were deleted this message".localized()+"_"
  5541. } else {
  5542. textChat = "🚫 _"+"This message was deleted".localized()+"_"
  5543. }
  5544. }
  5545. if dataMessages[indexPath.row]["lock"] as? String == "2" {
  5546. textChat = "🚫 _"+"Message has expired".localized()+"_"
  5547. }
  5548. if !audioChat.isEmpty {
  5549. textChat = textChat.components(separatedBy: "|")[0]
  5550. }
  5551. let imageSticker = UIImageView()
  5552. var stringLS = ""
  5553. if let attachmentFlag = dataMessages[indexPath.row]["attachment_flag"], let attachmentFlag = attachmentFlag as? String {
  5554. if attachmentFlag == "27" || attachmentFlag == "26" { // live streaming
  5555. let data = textChat
  5556. if let json = try! JSONSerialization.jsonObject(with: data.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
  5557. let title = json["title"] as? String ?? ""
  5558. let description = json["description"] as? String ?? ""
  5559. let start = json["time"] as? Int64 ?? 0
  5560. let by = json["by"] as? String ?? ""
  5561. let textLS = "Live Streaming".localized()
  5562. var type = "*\(textLS)*"
  5563. if attachmentFlag == "26" {
  5564. let textSeminar = "Seminar".localized()
  5565. type = "*\(textSeminar)*"
  5566. }
  5567. if let c = User.getData(pin: by) {
  5568. let name = c.fullName
  5569. stringLS = "\(type) \nTitle: \(title) \nDescription: \(description) \nStart: \(Date(milliseconds: start).format(dateFormat: "dd/MM/yyyy HH:mm")) \nBroadcaster: \(name)"
  5570. } else {
  5571. stringLS = ("\(type) \nTitle: \(title) \nDescription: \(description) \nStart: \(Date(milliseconds: start).format(dateFormat: "dd/MM/yyyy HH:mm"))")
  5572. }
  5573. messageText.attributedText = stringLS.richText()
  5574. messageText.isUserInteractionEnabled = false
  5575. }
  5576. }
  5577. else if attachmentFlag == "11" && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as? String ?? "" != "1") && (dataMessages[indexPath.row]["lock"] as? String != "2") {
  5578. messageText.text = ""
  5579. topMarginText.constant = topMarginText.constant + 100
  5580. containerMessage.addSubview(imageSticker)
  5581. imageSticker.translatesAutoresizingMaskIntoConstraints = false
  5582. let data = queryMessageReply(message_id: reffChat)
  5583. if reffChat.isEmpty || data.count == 0 {
  5584. imageSticker.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 15).isActive = true
  5585. imageSticker.widthAnchor.constraint(equalToConstant: 80).isActive = true
  5586. } else {
  5587. imageSticker.widthAnchor.constraint(greaterThanOrEqualToConstant: 80).isActive = true
  5588. }
  5589. imageSticker.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
  5590. imageSticker.bottomAnchor.constraint(equalTo: messageText.topAnchor, constant: -5).isActive = true
  5591. imageSticker.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  5592. var imageStickerBundle = UIImage(named: (textChat.components(separatedBy: "/")[1]), in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  5593. if imageStickerBundle == nil {
  5594. imageStickerBundle = UIImage(named: (textChat.components(separatedBy: "/")[1]), in: Bundle.resourcesMediaBundle(for: Nexilis.self), with: nil)
  5595. }
  5596. imageSticker.image = imageStickerBundle //resourcesMediaBundle
  5597. imageSticker.contentMode = .scaleAspectFit
  5598. } else if dataMessages[indexPath.row]["message_scope_id"] as? String ?? "" == "18" {
  5599. let data = textChat
  5600. if let jsonForm = try! JSONSerialization.jsonObject(with: data.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
  5601. let form_title = jsonForm["form_title"] as? String ?? ""
  5602. let club_type = jsonForm["club_type"] as? String ?? ""
  5603. let province = jsonForm["province"] as? String ?? ""
  5604. let club = jsonForm["club"] as? String ?? ""
  5605. messageText.attributedText = "*\(form_title.replacingOccurrences(of: "+", with: " "))* \nClub Type: \(club_type) \nProvince: \(province) \nClub Name: \(club) ".richText()
  5606. messageText.isUserInteractionEnabled = false
  5607. }
  5608. }
  5609. else {
  5610. messageText.attributedText = textChat.richText()
  5611. modifyText()
  5612. }
  5613. } else {
  5614. messageText.attributedText = textChat.richText()
  5615. modifyText()
  5616. }
  5617. func modifyText() {
  5618. if !textChat.isEmpty {
  5619. if textChat.contains("■"){
  5620. textChat = textChat.components(separatedBy: "■")[0]
  5621. textChat = textChat.trimmingCharacters(in: .whitespacesAndNewlines)
  5622. }
  5623. if !fileChat.isEmpty {
  5624. textChat = textChat.components(separatedBy: "|")[1]
  5625. }
  5626. let finalAtribute = textChat.richText()
  5627. textChat = finalAtribute.string
  5628. let urlPattern = "(https?://|www\\.)\\S+"
  5629. if let regex = try? NSRegularExpression(pattern: urlPattern, options: []) {
  5630. let matches = regex.matches(in: textChat, options: [], range: NSRange(textChat.startIndex..., in: textChat))
  5631. for match in matches {
  5632. if let range = Range(match.range, in: textChat) {
  5633. let linkText = String(textChat[range])
  5634. let nsRange = NSRange(range, in: textChat)
  5635. finalAtribute.addAttribute(.link, value: linkText, range: nsRange)
  5636. finalAtribute.addAttribute(.foregroundColor, value: UIColor.blue, range: nsRange)
  5637. finalAtribute.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: nsRange)
  5638. }
  5639. }
  5640. }
  5641. messageText.attributedText = finalAtribute
  5642. messageText.delegate = self
  5643. }
  5644. }
  5645. if !copySession && !forwardSession && !deleteSession && !self.removed {
  5646. let interaction = UIContextMenuInteraction(delegate: self)
  5647. containerMessage.addInteraction(interaction)
  5648. containerMessage.isUserInteractionEnabled = true
  5649. }
  5650. if isSearching && textSearch.count > 1 {
  5651. messageText.attributedText = stringLS.isEmpty ? textChat.richText(isSearching: true, textSearch: textSearch) : stringLS.richText(isSearching: true, textSearch: textSearch)
  5652. if textChat.lowercased().contains(textSearch) {
  5653. countMatchesSearch += 1
  5654. }
  5655. }
  5656. var stringDate = (dataMessages[indexPath.row]["server_date"] as? String) ?? ""
  5657. if dataMessages[indexPath.row][TypeDataMessage.last_edit] != nil && dataMessages[indexPath.row][TypeDataMessage.last_edit] as! Int64 != 0 {
  5658. stringDate = "\(dataMessages[indexPath.row][TypeDataMessage.last_edit] as! Int64)"
  5659. }
  5660. if !stringDate.isEmpty {
  5661. if (dataMessages[indexPath.row]["credential"] as? String) == "1" && dataMessages[indexPath.row]["lock"] as? String != "2" {
  5662. if dataTimer! >= 10 {
  5663. timeMessage.text = "00:\(dataTimer!)"
  5664. } else {
  5665. timeMessage.text = "00:0\(dataTimer!)"
  5666. }
  5667. timeMessage.textColor = .systemRed
  5668. } else {
  5669. let date = Date(milliseconds: Int64(stringDate) ?? 100)
  5670. let formatter = DateFormatter()
  5671. formatter.dateFormat = "HH:mm"
  5672. formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
  5673. timeMessage.text = formatter.string(from: date as Date)
  5674. timeMessage.textColor = .lightGray
  5675. }
  5676. timeMessage.font = UIFont.systemFont(ofSize: 10, weight: .medium)
  5677. }
  5678. let imageThumb = UIImageView()
  5679. let containerViewFile = UIView()
  5680. let imageGif = SDAnimatedImageView()
  5681. if !audioChat.isEmpty {
  5682. let imageAudio = UIImageView()
  5683. imageAudio.image = UIImage(systemName: "music.note", withConfiguration: UIImage.SymbolConfiguration(pointSize: 35))
  5684. containerMessage.addSubview(imageAudio)
  5685. imageAudio.anchor(left: containerMessage.leftAnchor, paddingLeft: 15, centerY: containerMessage.centerYAnchor)
  5686. imageAudio.tintColor = .black
  5687. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  5688. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  5689. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  5690. if let dirPath = paths.first {
  5691. let audioURL = URL(fileURLWithPath: dirPath).appendingPathComponent(audioChat)
  5692. if !FileManager.default.fileExists(atPath: audioURL.path) && !FileEncryption.shared.isSecureExists(filename: audioChat) {
  5693. Download().startHTTP(forKey: audioChat, isImage: false) { (name, progress) in
  5694. guard progress == 100 else {
  5695. return
  5696. }
  5697. tableView.reloadRows(at: [indexPath], with: .none)
  5698. }
  5699. }
  5700. }
  5701. let objectTap = ObjectGesture(target: self, action: #selector(contentMessageTapped(_:)))
  5702. containerMessage.addGestureRecognizer(objectTap)
  5703. objectTap.audio_id = audioChat
  5704. }
  5705. if (!thumbChat.isEmpty && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as? String ?? "" != "1") && (dataMessages[indexPath.row]["lock"] as? String != "2")) {
  5706. if let listImages = groupImages[messageIdChat] {
  5707. timeMessage.isHidden = true
  5708. statusMessage.isHidden = true
  5709. imageStared.isHidden = true
  5710. topMarginText.constant = topMarginText.constant + 225
  5711. let listImageThumb: [UIImageView] = [UIImageView(), UIImageView(), UIImageView(), UIImageView()]
  5712. for i in 0..<4 {
  5713. containerMessage.addSubview(listImageThumb[i])
  5714. listImageThumb[i].layer.cornerRadius = 5.0
  5715. listImageThumb[i].clipsToBounds = true
  5716. listImageThumb[i].contentMode = .scaleAspectFill
  5717. let widthHeightImage: CGFloat = 120
  5718. switch i {
  5719. case 0:
  5720. listImageThumb[i].anchor(top: containerMessage.topAnchor, left: containerMessage.leftAnchor, paddingTop: 5, paddingLeft: 5, width: widthHeightImage, height: widthHeightImage)
  5721. case 1:
  5722. listImageThumb[i].anchor(top: containerMessage.topAnchor, left: listImageThumb[0].rightAnchor, right: containerMessage.rightAnchor, paddingTop: 5, paddingLeft: 5, paddingRight: 5, width: widthHeightImage, height: widthHeightImage)
  5723. case 2:
  5724. listImageThumb[i].anchor(left: containerMessage.leftAnchor, bottom: containerMessage.bottomAnchor, paddingLeft: 5, paddingBottom: 5, width: widthHeightImage, height: widthHeightImage)
  5725. default:
  5726. listImageThumb[i].anchor(left: listImageThumb[2].rightAnchor, bottom: containerMessage.bottomAnchor, right: containerMessage.rightAnchor, paddingLeft: 5, paddingBottom: 5, paddingRight: 5, width: widthHeightImage, height: widthHeightImage)
  5727. }
  5728. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  5729. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  5730. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  5731. if let dirPath = paths.first {
  5732. let thumbURL = URL(fileURLWithPath: dirPath).appendingPathComponent(listImages[i].thumbId)
  5733. let image : UIImage? = {
  5734. if let img = Nexilis.imageCache.object(forKey: listImages[i].thumbId as NSString) {
  5735. return img
  5736. }
  5737. else if let img = UIImage(contentsOfFile: thumbURL.path)?.resize(target: CGSize(width: 500, height: 500)) {
  5738. Nexilis.imageCache.setObject(img, forKey: listImages[i].thumbId as NSString)
  5739. return img
  5740. }
  5741. return nil
  5742. }()
  5743. // let image = UIGraphicsRenderer.renderImageAt(url: thumbURL as NSURL, size: CGSize(width: 250, height: 250))
  5744. listImageThumb[i].image = image
  5745. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(listImages[i].imageId)
  5746. if !FileManager.default.fileExists(atPath: imageURL.path) && !FileEncryption.shared.isSecureExists(filename: imageURL.lastPathComponent) {
  5747. let blurEffect = UIBlurEffect(style: UIBlurEffect.Style.light)
  5748. let blurEffectView = UIVisualEffectView(effect: blurEffect)
  5749. blurEffectView.frame = CGRect(x: 0, y: 0, width: listImageThumb[i].frame.size.width, height: listImageThumb[i].frame.size.height)
  5750. blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  5751. listImageThumb[i].addSubview(blurEffectView)
  5752. }
  5753. }
  5754. let containerTimeStatus = UIView()
  5755. listImageThumb[i].addSubview(containerTimeStatus)
  5756. containerTimeStatus.anchor(bottom: listImageThumb[i].bottomAnchor, right: listImageThumb[i].rightAnchor, height: 15)
  5757. let widthcontainerTimeStatus = containerTimeStatus.widthAnchor.constraint(equalToConstant: 50)
  5758. widthcontainerTimeStatus.isActive = true
  5759. containerTimeStatus.layer.cornerRadius = 5.0
  5760. containerTimeStatus.layer.masksToBounds = true
  5761. containerTimeStatus.backgroundColor = .black.withAlphaComponent(0.15)
  5762. let timeInImage = UILabel()
  5763. containerTimeStatus.addSubview(timeInImage)
  5764. let date = Date(milliseconds: Int64(listImages[i].time) ?? 100)
  5765. let formatter = DateFormatter()
  5766. formatter.dateFormat = "HH:mm"
  5767. formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
  5768. timeInImage.text = formatter.string(from: date as Date)
  5769. timeInImage.textColor = .white
  5770. timeInImage.font = UIFont.systemFont(ofSize: 10, weight: .medium)
  5771. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  5772. let statusInImage = UIImageView()
  5773. containerTimeStatus.addSubview(statusInImage)
  5774. statusInImage.anchor(right: containerTimeStatus.rightAnchor, centerY: containerTimeStatus.centerYAnchor, width: 15, height: 15)
  5775. if listImages[i].status == "0" {
  5776. statusMessage.image = UIImage(systemName: "xmark.circle")!.withTintColor(UIColor.red, renderingMode: .alwaysOriginal)
  5777. } else if listImages[i].status == "1" {
  5778. statusInImage.image = UIImage(systemName: "clock.arrow.circlepath")!.withTintColor(UIColor.white, renderingMode: .alwaysOriginal)
  5779. } else if listImages[i].status == "2" {
  5780. statusInImage.image = UIImage(named: "checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.white)
  5781. } else if listImages[i].status == "3" {
  5782. statusInImage.image = UIImage(named: "double-checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.white)
  5783. } else {
  5784. statusInImage.image = UIImage(named: "double-checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.systemBlue)
  5785. }
  5786. timeInImage.anchor(right: statusInImage.leftAnchor, centerY: containerTimeStatus.centerYAnchor, height: 15)
  5787. } else {
  5788. timeInImage.anchor(right: containerTimeStatus.rightAnchor, paddingRight: 5, centerY: containerTimeStatus.centerYAnchor, height: 15)
  5789. widthcontainerTimeStatus.constant = widthcontainerTimeStatus.constant - 10
  5790. }
  5791. if listImages[i].dataMessage["is_stared"] as? String == "1" {
  5792. let iconStar = UIImageView()
  5793. containerTimeStatus.addSubview(iconStar)
  5794. iconStar.anchor(right: timeInImage.leftAnchor, paddingRight: 2, centerY: containerTimeStatus.centerYAnchor, width: 15, height: 15)
  5795. widthcontainerTimeStatus.constant = widthcontainerTimeStatus.constant + 15
  5796. iconStar.image = UIImage(systemName: "star.fill")
  5797. iconStar.tintColor = .white
  5798. }
  5799. if !copySession && !forwardSession && !deleteSession {
  5800. let objectTap = ObjectGesture(target: self, action: #selector(imageGroupingTapped(_:)))
  5801. listImageThumb[i].isUserInteractionEnabled = true
  5802. listImageThumb[i].addGestureRecognizer(objectTap)
  5803. objectTap.indexImageTapped = i
  5804. objectTap.listImageFromGrouping = listImages
  5805. objectTap.isInitiator = dataMessages[indexPath.row]["f_pin"] as? String == idMe
  5806. }
  5807. }
  5808. if listImages.count > 4 {
  5809. let blurEffect = UIBlurEffect(style: UIBlurEffect.Style.dark)
  5810. let blurEffectView = UIVisualEffectView(effect: blurEffect)
  5811. blurEffectView.frame = CGRect(x: 0, y: 0, width: listImageThumb[3].frame.size.width, height: listImageThumb[3].frame.size.height)
  5812. blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  5813. listImageThumb[3].addSubview(blurEffectView)
  5814. let countRestImages = UILabel()
  5815. listImageThumb[3].addSubview(countRestImages)
  5816. countRestImages.anchor(centerX: listImageThumb[3].centerXAnchor, centerY: listImageThumb[3].centerYAnchor)
  5817. countRestImages.font = UIFont.systemFont(ofSize: 30, weight: .medium)
  5818. countRestImages.text = "+\(listImages.count - 3)"
  5819. countRestImages.textColor = .white
  5820. }
  5821. } else {
  5822. let getHeightImage: CGFloat = ListGroupImages.getImageSize(image: thumbChat, screenWidth: self.view.frame.size.width * 0.6, screenHeight: 305)!.height
  5823. let getWidthImage: CGFloat = ListGroupImages.getImageSize(image: thumbChat, screenWidth: self.view.frame.size.width * 0.6, screenHeight: 305)!.width
  5824. topMarginText.constant = topMarginText.constant + (getHeightImage < 40 ? 45 : getHeightImage + 5)
  5825. containerMessage.addSubview(imageThumb)
  5826. imageThumb.frame = CGRect(x: 0, y: 0, width: getWidthImage, height: getHeightImage)
  5827. imageThumb.translatesAutoresizingMaskIntoConstraints = false
  5828. let data = queryMessageReply(message_id: reffChat)
  5829. if (reffChat.isEmpty || data.count == 0) && (dataMessages[indexPath.row][TypeDataMessage.is_forwarded] == nil || dataMessages[indexPath.row][TypeDataMessage.is_forwarded] as! Int == 0) {
  5830. imageThumb.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 15).isActive = true
  5831. }
  5832. imageThumb.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
  5833. imageThumb.bottomAnchor.constraint(equalTo: messageText.topAnchor, constant: -5).isActive = true
  5834. imageThumb.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  5835. imageThumb.widthAnchor.constraint(equalToConstant: getWidthImage).isActive = true
  5836. imageThumb.layer.cornerRadius = 5.0
  5837. imageThumb.clipsToBounds = true
  5838. imageThumb.contentMode = .scaleAspectFill
  5839. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  5840. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  5841. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  5842. if let dirPath = paths.first {
  5843. let thumbURL = URL(fileURLWithPath: dirPath).appendingPathComponent(thumbChat)
  5844. DispatchQueue.main.async {
  5845. let image : UIImage? = {
  5846. if let img = Nexilis.imageCache.object(forKey: thumbChat as NSString) {
  5847. return img
  5848. }
  5849. else if let img = UIImage(contentsOfFile: thumbURL.path)?.resize(target: CGSize(width: 500, height: 500)) {
  5850. Nexilis.imageCache.setObject(img, forKey: thumbChat as NSString)
  5851. return img
  5852. }
  5853. return nil
  5854. }()
  5855. imageThumb.image = image
  5856. }
  5857. // let image = UIGraphicsRenderer.renderImageAt(url: thumbURL as NSURL, size: CGSize(width: 250, height: 250))
  5858. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(imageChat)
  5859. if !FileManager.default.fileExists(atPath: imageURL.path) && !FileEncryption.shared.isSecureExists(filename: imageURL.lastPathComponent) {
  5860. let blurEffect = UIBlurEffect(style: UIBlurEffect.Style.light)
  5861. let blurEffectView = UIVisualEffectView(effect: blurEffect)
  5862. blurEffectView.frame = CGRect(x: 0, y: 0, width: imageThumb.frame.size.width, height: imageThumb.frame.size.height)
  5863. blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  5864. imageThumb.addSubview(blurEffectView)
  5865. if !imageChat.isEmpty {
  5866. let imageDownload = UIImageView(image: UIImage(systemName: "arrow.down.circle.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 50, weight: .bold, scale: .default)))
  5867. imageThumb.addSubview(imageDownload)
  5868. imageDownload.tintColor = .black.withAlphaComponent(0.3)
  5869. imageDownload.translatesAutoresizingMaskIntoConstraints = false
  5870. imageDownload.centerXAnchor.constraint(equalTo: imageThumb.centerXAnchor).isActive = true
  5871. imageDownload.centerYAnchor.constraint(equalTo: imageThumb.centerYAnchor).isActive = true
  5872. }
  5873. }
  5874. }
  5875. if (videoChat != "" && gifChat.isEmpty) {
  5876. let imagePlay = UIImageView(image: UIImage(systemName: "play.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .bold, scale: .default))?.imageWithInsets(insets: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10))?.withTintColor(.white))
  5877. imagePlay.circle()
  5878. imageThumb.addSubview(imagePlay)
  5879. imagePlay.backgroundColor = .black.withAlphaComponent(0.3)
  5880. imagePlay.translatesAutoresizingMaskIntoConstraints = false
  5881. imagePlay.centerXAnchor.constraint(equalTo: imageThumb.centerXAnchor).isActive = true
  5882. imagePlay.centerYAnchor.constraint(equalTo: imageThumb.centerYAnchor).isActive = true
  5883. } else if !gifChat.isEmpty {
  5884. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  5885. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  5886. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  5887. if let dirPath = paths.first {
  5888. let gifURL = URL(fileURLWithPath: dirPath).appendingPathComponent(gifChat)
  5889. if !FileManager.default.fileExists(atPath: gifURL.path) && !FileEncryption.shared.isSecureExists(filename: gifChat) {
  5890. Download().startHTTP(forKey: gifChat, isImage: false) { (name, progress) in
  5891. guard progress == 100 else {
  5892. return
  5893. }
  5894. tableView.reloadRows(at: [indexPath], with: .none)
  5895. }
  5896. } else {
  5897. imageThumb.addSubview(imageGif)
  5898. imageGif.translatesAutoresizingMaskIntoConstraints = false
  5899. imageGif.anchor(top: imageThumb.topAnchor, left: imageThumb.leftAnchor, bottom: imageThumb.bottomAnchor, right: imageThumb.rightAnchor)
  5900. if FileManager.default.fileExists(atPath: gifURL.path) {
  5901. imageGif.image = SDAnimatedImage(contentsOfFile: gifURL.path)
  5902. // imageGif.shouldCustomLoopCount = true
  5903. // imageGif.animationRepeatCount = 4
  5904. } else if FileEncryption.shared.isSecureExists(filename: gifChat){
  5905. do {
  5906. let data = try FileEncryption.shared.readSecure(filename: gifChat)
  5907. if let imageData = SDAnimatedImage(data: data!) {
  5908. imageGif.image = imageData
  5909. // imageGif.shouldCustomLoopCount = true
  5910. // imageGif.animationRepeatCount = 4
  5911. }
  5912. }
  5913. catch {
  5914. print("Error reading secure file")
  5915. }
  5916. }
  5917. }
  5918. }
  5919. }
  5920. if (dataMessages[indexPath.row]["progress"] as! Double != 100.0 && dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  5921. let container = UIView()
  5922. imageThumb.addSubview(container)
  5923. container.translatesAutoresizingMaskIntoConstraints = false
  5924. container.bottomAnchor.constraint(equalTo: imageThumb.bottomAnchor, constant: -10).isActive = true
  5925. container.leadingAnchor.constraint(equalTo: imageThumb.leadingAnchor, constant: 10).isActive = true
  5926. container.widthAnchor.constraint(equalToConstant: 30).isActive = true
  5927. container.heightAnchor.constraint(equalToConstant: 30).isActive = true
  5928. container.backgroundColor = .white.withAlphaComponent(0.1)
  5929. let circlePath = UIBezierPath(arcCenter: CGPoint(x: 10, y: 20), radius: 15, startAngle: -(.pi / 2), endAngle: .pi * 2, clockwise: true)
  5930. let trackShape = CAShapeLayer()
  5931. trackShape.path = circlePath.cgPath
  5932. trackShape.fillColor = UIColor.black.withAlphaComponent(0.3).cgColor
  5933. trackShape.lineWidth = 3
  5934. trackShape.strokeColor = UIColor.blueBubbleColor.withAlphaComponent(0.3).cgColor
  5935. container.backgroundColor = .clear
  5936. container.layer.addSublayer(trackShape)
  5937. let shapeLoading = CAShapeLayer()
  5938. shapeLoading.path = circlePath.cgPath
  5939. shapeLoading.fillColor = UIColor.clear.cgColor
  5940. shapeLoading.lineWidth = 3
  5941. shapeLoading.strokeEnd = 0
  5942. shapeLoading.strokeColor = UIColor.blueBubbleColor.cgColor
  5943. container.layer.addSublayer(shapeLoading)
  5944. let imageupload = UIImageView(image: UIImage(systemName: "arrow.up", withConfiguration: UIImage.SymbolConfiguration(pointSize: 10, weight: .bold, scale: .default)))
  5945. imageupload.tintColor = .white
  5946. container.addSubview(imageupload)
  5947. imageupload.translatesAutoresizingMaskIntoConstraints = false
  5948. imageupload.bottomAnchor.constraint(equalTo: imageThumb.bottomAnchor, constant: -10).isActive = true
  5949. imageupload.leadingAnchor.constraint(equalTo: imageThumb.leadingAnchor, constant: 10).isActive = true
  5950. imageupload.widthAnchor.constraint(equalToConstant: 20).isActive = true
  5951. imageupload.heightAnchor.constraint(equalToConstant: 20).isActive = true
  5952. }
  5953. if !copySession && !forwardSession && !deleteSession {
  5954. let objectTap = ObjectGesture(target: self, action: #selector(contentMessageTapped(_:)))
  5955. imageThumb.isUserInteractionEnabled = true
  5956. imageThumb.addGestureRecognizer(objectTap)
  5957. objectTap.image_id = imageChat
  5958. objectTap.video_id = videoChat
  5959. objectTap.gif_id = gifChat
  5960. objectTap.imageView = imageThumb
  5961. objectTap.indexPath = indexPath
  5962. }
  5963. }
  5964. }
  5965. if (fileChat != "" && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as? String ?? "" != "1") && dataMessages[indexPath.row]["message_scope_id"] as? String ?? "" != "18" && (dataMessages[indexPath.row]["lock"] as? String != "2")) {
  5966. topMarginText.constant = topMarginText.constant + 55
  5967. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  5968. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  5969. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  5970. let arrExtFile = (originalMessageText.components(separatedBy: "|")[0]).split(separator: ".")
  5971. let finalExtFile = arrExtFile[arrExtFile.count - 1]
  5972. if let dirPath = paths.first {
  5973. let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(fileChat)
  5974. if FileManager.default.fileExists(atPath: fileURL.path) {
  5975. if let dataFile = try? Data(contentsOf: fileURL), textChat.isEmpty {
  5976. var sizeOfFile = Int(dataFile.count / 1000000)
  5977. if (sizeOfFile < 1) {
  5978. sizeOfFile = Int(dataFile.count / 1000)
  5979. if (finalExtFile.count > 4) {
  5980. messageText.text = "\(sizeOfFile) kB \u{2022} TXT"
  5981. }else {
  5982. messageText.text = "\(sizeOfFile) kB \u{2022} \(finalExtFile.uppercased())"
  5983. }
  5984. } else {
  5985. if (finalExtFile.count > 4) {
  5986. messageText.text = "\(sizeOfFile) MB \u{2022} TXT"
  5987. }else {
  5988. messageText.text = "\(sizeOfFile) MB \u{2022} \(finalExtFile.uppercased())"
  5989. }
  5990. }
  5991. }
  5992. }
  5993. else if FileEncryption.shared.isSecureExists(filename: fileChat) {
  5994. if let dataFile = try? FileEncryption.shared.readSecure(filename: fileChat), textChat.isEmpty {
  5995. var sizeOfFile = Int(dataFile.count / 1000000)
  5996. if (sizeOfFile < 1) {
  5997. sizeOfFile = Int(dataFile.count / 1000)
  5998. if (finalExtFile.count > 4) {
  5999. messageText.text = "\(sizeOfFile) kB \u{2022} TXT"
  6000. }else {
  6001. messageText.text = "\(sizeOfFile) kB \u{2022} \(finalExtFile.uppercased())"
  6002. }
  6003. } else {
  6004. if (finalExtFile.count > 4) {
  6005. messageText.text = "\(sizeOfFile) MB \u{2022} TXT"
  6006. }else {
  6007. messageText.text = "\(sizeOfFile) MB \u{2022} \(finalExtFile.uppercased())"
  6008. }
  6009. }
  6010. }
  6011. }
  6012. }
  6013. containerMessage.addSubview(containerViewFile)
  6014. containerViewFile.translatesAutoresizingMaskIntoConstraints = false
  6015. let data = queryMessageReply(message_id: reffChat)
  6016. if (reffChat.isEmpty || data.count == 0) && (dataMessages[indexPath.row][TypeDataMessage.is_forwarded] == nil || dataMessages[indexPath.row][TypeDataMessage.is_forwarded] as! Int == 0) {
  6017. containerViewFile.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 15).isActive = true
  6018. }
  6019. containerViewFile.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
  6020. containerViewFile.bottomAnchor.constraint(equalTo:messageText.topAnchor, constant: -5).isActive = true
  6021. containerViewFile.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  6022. // containerViewFile.heightAnchor.constraint(equalToConstant: 50).isActive = true
  6023. containerViewFile.backgroundColor = .black.withAlphaComponent(0.2)
  6024. containerViewFile.layer.cornerRadius = 5.0
  6025. containerViewFile.clipsToBounds = true
  6026. let imageFile = UIImageView(image: UIImage(systemName: "doc.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 30, weight: .bold, scale: .default)))
  6027. containerViewFile.addSubview(imageFile)
  6028. let nameFile = UILabel()
  6029. containerViewFile.addSubview(nameFile)
  6030. imageFile.translatesAutoresizingMaskIntoConstraints = false
  6031. imageFile.leadingAnchor.constraint(equalTo: containerViewFile.leadingAnchor, constant: 5).isActive = true
  6032. imageFile.trailingAnchor.constraint(equalTo: nameFile.leadingAnchor, constant: -5).isActive = true
  6033. imageFile.centerYAnchor.constraint(equalTo: containerViewFile.centerYAnchor).isActive = true
  6034. imageFile.widthAnchor.constraint(equalToConstant: 30).isActive = true
  6035. imageFile.heightAnchor.constraint(equalToConstant: 30).isActive = true
  6036. imageFile.tintColor = .docColor
  6037. nameFile.translatesAutoresizingMaskIntoConstraints = false
  6038. nameFile.centerYAnchor.constraint(equalTo: containerViewFile.centerYAnchor).isActive = true
  6039. nameFile.widthAnchor.constraint(lessThanOrEqualToConstant: 200).isActive = true
  6040. nameFile.font = UIFont.systemFont(ofSize: 12, weight: .medium)
  6041. nameFile.textColor = .white
  6042. nameFile.text = originalMessageText.components(separatedBy: "|")[0]
  6043. if (dataMessages[indexPath.row]["progress"] as! Double != 100.0) {
  6044. let containerLoading = UIView()
  6045. containerViewFile.addSubview(containerLoading)
  6046. containerLoading.translatesAutoresizingMaskIntoConstraints = false
  6047. containerLoading.centerYAnchor.constraint(equalTo: containerViewFile.centerYAnchor).isActive = true
  6048. containerLoading.leadingAnchor.constraint(equalTo: nameFile.trailingAnchor, constant: 5).isActive = true
  6049. containerLoading.trailingAnchor.constraint(equalTo: containerViewFile.trailingAnchor, constant: -5).isActive = true
  6050. containerLoading.widthAnchor.constraint(equalToConstant: 30).isActive = true
  6051. containerLoading.heightAnchor.constraint(equalToConstant: 30).isActive = true
  6052. let circlePath = UIBezierPath(arcCenter: CGPoint(x: 15, y: 15), radius: 10, startAngle: -(.pi / 2), endAngle: .pi * 2, clockwise: true)
  6053. let trackShape = CAShapeLayer()
  6054. trackShape.path = circlePath.cgPath
  6055. trackShape.fillColor = UIColor.clear.cgColor
  6056. trackShape.lineWidth = 5
  6057. trackShape.strokeColor = UIColor.blueBubbleColor.withAlphaComponent(0.3).cgColor
  6058. containerLoading.layer.addSublayer(trackShape)
  6059. let shapeLoading = CAShapeLayer()
  6060. shapeLoading.path = circlePath.cgPath
  6061. shapeLoading.fillColor = UIColor.clear.cgColor
  6062. shapeLoading.lineWidth = 3
  6063. shapeLoading.strokeEnd = 0
  6064. shapeLoading.strokeColor = UIColor.secondaryColor.cgColor
  6065. containerLoading.layer.addSublayer(shapeLoading)
  6066. var imageupload = UIImageView(image: UIImage(systemName: "arrow.up", withConfiguration: UIImage.SymbolConfiguration(pointSize: 10, weight: .bold, scale: .default)))
  6067. if dataMessages[indexPath.row]["f_pin"] as? String != idMe {
  6068. imageupload = UIImageView(image: UIImage(systemName: "arrow.down", withConfiguration: UIImage.SymbolConfiguration(pointSize: 10, weight: .bold, scale: .default)))
  6069. shapeLoading.strokeColor = UIColor.blueBubbleColor.cgColor
  6070. }
  6071. imageupload.tintColor = .white
  6072. containerLoading.addSubview(imageupload)
  6073. imageupload.translatesAutoresizingMaskIntoConstraints = false
  6074. imageupload.centerYAnchor.constraint(equalTo: containerLoading.centerYAnchor).isActive = true
  6075. imageupload.centerXAnchor.constraint(equalTo: containerLoading.centerXAnchor).isActive = true
  6076. } else {
  6077. nameFile.trailingAnchor.constraint(equalTo: containerViewFile.trailingAnchor, constant: -5).isActive = true
  6078. }
  6079. if !copySession && !forwardSession && !deleteSession {
  6080. let objectTap = ObjectGesture(target: self, action: #selector(contentMessageTapped(_:)))
  6081. containerViewFile.addGestureRecognizer(objectTap)
  6082. objectTap.containerFile = containerViewFile
  6083. objectTap.labelFile = nameFile
  6084. objectTap.file_id = fileChat
  6085. objectTap.indexPath = indexPath
  6086. }
  6087. }
  6088. let containerLinkMessage = UIView()
  6089. var isLoadingShowLink = false
  6090. if thumbChat.isEmpty && fileChat.isEmpty && !textChat.isEmpty {
  6091. var text = ""
  6092. let listTextSplitBreak = textChat.components(separatedBy: "\n")
  6093. let indexFirstLinkSplitBreak = listTextSplitBreak.firstIndex(where: { $0.contains("www.") || $0.contains("http://") || $0.contains("https://") })
  6094. if indexFirstLinkSplitBreak != nil {
  6095. let listTextSplitSpace = listTextSplitBreak[indexFirstLinkSplitBreak!].components(separatedBy: " ")
  6096. let indexFirstLinkSplitSpace = listTextSplitSpace.firstIndex(where: { ($0.starts(with: "www.") && $0.components(separatedBy: ".").count > 2) || ($0.starts(with: "http://") && $0.components(separatedBy: ".").count > 1) || ($0.starts(with: "https://") && $0.components(separatedBy: ".").count > 1) })
  6097. if indexFirstLinkSplitSpace != nil {
  6098. text = listTextSplitSpace[indexFirstLinkSplitSpace!]
  6099. }
  6100. }
  6101. if !text.isEmpty {
  6102. isLoadingShowLink = true
  6103. func showLink() {
  6104. if let data = try! JSONSerialization.jsonObject(with: dataURL.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
  6105. let title = data["title"] as? String ?? ""
  6106. let description = data["description"] as? String ?? ""
  6107. let imageUrl = data["imageUrl"] as? String
  6108. let link = data["link"] as? String ?? ""
  6109. topMarginText.constant = topMarginText.constant + 85
  6110. containerMessage.addSubview(containerLinkMessage)
  6111. containerLinkMessage.translatesAutoresizingMaskIntoConstraints = false
  6112. containerLinkMessage.leadingAnchor.constraint(equalTo:containerMessage.leadingAnchor, constant: 15).isActive = true
  6113. containerLinkMessage.bottomAnchor.constraint(equalTo: messageText.topAnchor, constant: -5).isActive = true
  6114. containerLinkMessage.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  6115. containerLinkMessage.heightAnchor.constraint(equalToConstant: 80.0).isActive = true
  6116. containerLinkMessage.backgroundColor = .gray.withAlphaComponent(0.2)
  6117. let imagePreview = UIImageView()
  6118. if imageUrl != nil {
  6119. containerLinkMessage.addSubview(imagePreview)
  6120. imagePreview.translatesAutoresizingMaskIntoConstraints = false
  6121. imagePreview.leadingAnchor.constraint(equalTo: containerLinkMessage.leadingAnchor).isActive = true
  6122. imagePreview.bottomAnchor.constraint(equalTo: containerLinkMessage.bottomAnchor).isActive = true
  6123. imagePreview.topAnchor.constraint(equalTo: containerLinkMessage.topAnchor).isActive = true
  6124. imagePreview.widthAnchor.constraint(equalToConstant: 80.0).isActive = true
  6125. imagePreview.loadImageAsync(with: imageUrl)
  6126. imagePreview.contentMode = .scaleToFill
  6127. }
  6128. let titlePreview = UILabel()
  6129. containerLinkMessage.addSubview(titlePreview)
  6130. titlePreview.translatesAutoresizingMaskIntoConstraints = false
  6131. if imageUrl != nil {
  6132. titlePreview.leadingAnchor.constraint(equalTo: imagePreview.trailingAnchor, constant: 5.0).isActive = true
  6133. } else {
  6134. titlePreview.leadingAnchor.constraint(equalTo: containerLinkMessage.leadingAnchor, constant: 5.0).isActive = true
  6135. }
  6136. titlePreview.topAnchor.constraint(equalTo: containerLinkMessage.topAnchor, constant: 10.0).isActive = true
  6137. titlePreview.trailingAnchor.constraint(equalTo: containerLinkMessage.trailingAnchor, constant: -5.0).isActive = true
  6138. titlePreview.text = title
  6139. titlePreview.font = UIFont.systemFont(ofSize: 14.0, weight: .bold)
  6140. titlePreview.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
  6141. let descPreview = UILabel()
  6142. containerLinkMessage.addSubview(descPreview)
  6143. descPreview.translatesAutoresizingMaskIntoConstraints = false
  6144. if imageUrl != nil {
  6145. descPreview.leadingAnchor.constraint(equalTo: imagePreview.trailingAnchor, constant: 5.0).isActive = true
  6146. } else {
  6147. descPreview.leadingAnchor.constraint(equalTo: containerLinkMessage.leadingAnchor, constant: 5.0).isActive = true
  6148. }
  6149. descPreview.topAnchor.constraint(equalTo: titlePreview.bottomAnchor).isActive = true
  6150. descPreview.trailingAnchor.constraint(equalTo: containerLinkMessage.trailingAnchor, constant: -5.0).isActive = true
  6151. descPreview.text = description
  6152. descPreview.font = UIFont.systemFont(ofSize: 12.0)
  6153. descPreview.textColor = .gray
  6154. descPreview.numberOfLines = 1
  6155. let linkPreview = UILabel()
  6156. containerLinkMessage.addSubview(linkPreview)
  6157. linkPreview.translatesAutoresizingMaskIntoConstraints = false
  6158. if imageUrl != nil {
  6159. linkPreview.leadingAnchor.constraint(equalTo: imagePreview.trailingAnchor, constant: 5.0).isActive = true
  6160. } else {
  6161. linkPreview.leadingAnchor.constraint(equalTo: containerLinkMessage.leadingAnchor, constant: 5.0).isActive = true
  6162. }
  6163. linkPreview.topAnchor.constraint(equalTo: descPreview.bottomAnchor, constant: 8.0).isActive = true
  6164. linkPreview.trailingAnchor.constraint(equalTo: containerLinkMessage.trailingAnchor, constant: -5.0).isActive = true
  6165. linkPreview.text = link
  6166. linkPreview.font = UIFont.systemFont(ofSize: 10.0)
  6167. linkPreview.textColor = .gray
  6168. linkPreview.numberOfLines = 1
  6169. if dataMessages[indexPath.row][TypeDataMessage.is_forwarded] != nil && dataMessages[indexPath.row][TypeDataMessage.is_forwarded] as! Int != 0 {
  6170. showForwardedSign()
  6171. }
  6172. if !copySession && !forwardSession && !deleteSession {
  6173. let objectTap = ObjectGesture(target: self, action: #selector(tapMessageText(_:)))
  6174. objectTap.message_id = text
  6175. containerLinkMessage.addGestureRecognizer(objectTap)
  6176. }
  6177. }
  6178. }
  6179. var dataURL = ""
  6180. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  6181. do {
  6182. if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "select data_link from LINK_PREVIEW where link='\(text)'"), cursor.next() {
  6183. if let data = cursor.string(forColumnIndex: 0) {
  6184. dataURL = data
  6185. }
  6186. cursor.close()
  6187. }
  6188. } catch {
  6189. rollback.pointee = true
  6190. print("Access database error: \(error.localizedDescription)")
  6191. }
  6192. })
  6193. if dataURL.isEmpty {
  6194. let urlConfig = URLSessionConfiguration.default
  6195. let sessionDelegate = SelfSignedURLSessionDelegate()
  6196. let session = URLSession(configuration: urlConfig, delegate: sessionDelegate, delegateQueue: nil)
  6197. let slp = SwiftLinkPreview(session: session,
  6198. workQueue: SwiftLinkPreview.defaultWorkQueue,
  6199. responseQueue: DispatchQueue.main,
  6200. cache: DisabledCache.instance)
  6201. let preview = slp.preview(text,
  6202. onSuccess: { result in
  6203. let title = result.title ?? "No Title"
  6204. let description = text.contains("google.com") ? "" : result.description
  6205. let imageUrl = result.icon
  6206. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  6207. do {
  6208. var dataJson: [String: Any] = [:]
  6209. dataJson["title"] = title
  6210. dataJson["description"] = description
  6211. dataJson["imageUrl"] = imageUrl
  6212. dataJson["link"] = text
  6213. guard let json = String(data: try! JSONSerialization.data(withJSONObject: dataJson, options: []), encoding: String.Encoding.utf8) else {
  6214. return
  6215. }
  6216. _ = try Database.shared.insertRecord(fmdb: fmdb, table: "LINK_PREVIEW", cvalues: [
  6217. "id" : "\(Date().currentTimeMillis().toHex())",
  6218. "link" : text,
  6219. "data_link" : json,
  6220. "retry": 0
  6221. ], replace: true)
  6222. dataURL = json
  6223. showLink()
  6224. DispatchQueue.main.async {
  6225. tableView.reloadRows(at: [indexPath], with: .none)
  6226. }
  6227. } catch {
  6228. rollback.pointee = true
  6229. print("Access database error: \(error.localizedDescription)")
  6230. }
  6231. })
  6232. }, onError: { error in
  6233. })
  6234. } else {
  6235. showLink()
  6236. }
  6237. }
  6238. }
  6239. if (reffChat != "" && dataMessages[indexPath.row]["message_scope_id"] as? String ?? "" != "18") {
  6240. let data = queryMessageReply(message_id: reffChat)
  6241. if data.count != 0 {
  6242. topMarginText.constant = topMarginText.constant + 55
  6243. let containerReply = UIView()
  6244. containerMessage.addSubview(containerReply)
  6245. containerReply.translatesAutoresizingMaskIntoConstraints = false
  6246. containerReply.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
  6247. containerReply.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 15).isActive = true
  6248. if thumbChat != "" && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as? String ?? "" != "1") {
  6249. containerReply.bottomAnchor.constraint(equalTo: imageThumb.topAnchor, constant: -5).isActive = true
  6250. } else if fileChat != "" && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as? String ?? "" != "1") {
  6251. containerReply.bottomAnchor.constraint(equalTo: containerViewFile.topAnchor, constant: -5).isActive = true
  6252. } else if containerMessage.subviews.contains(containerLinkMessage) {
  6253. containerReply.bottomAnchor.constraint(equalTo: containerLinkMessage.topAnchor, constant: -5).isActive = true
  6254. } else if dataMessages[indexPath.row]["attachment_flag"] as? String == "11" && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as? String ?? "" != "1") {
  6255. containerReply.bottomAnchor.constraint(equalTo: imageSticker.topAnchor, constant: -5).isActive = true
  6256. } else {
  6257. containerReply.bottomAnchor.constraint(equalTo: messageText.topAnchor, constant: -5).isActive = true
  6258. }
  6259. containerReply.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  6260. containerReply.heightAnchor.constraint(equalToConstant: 50).isActive = true
  6261. containerReply.backgroundColor = .black.withAlphaComponent(0.2)
  6262. containerReply.layer.cornerRadius = 5
  6263. containerReply.clipsToBounds = true
  6264. let leftReply = UIView()
  6265. containerReply.addSubview(leftReply)
  6266. leftReply.translatesAutoresizingMaskIntoConstraints = false
  6267. leftReply.leadingAnchor.constraint(equalTo: containerReply.leadingAnchor).isActive = true
  6268. leftReply.topAnchor.constraint(equalTo: containerReply.topAnchor).isActive = true
  6269. leftReply.bottomAnchor.constraint(equalTo: containerReply.bottomAnchor).isActive = true
  6270. leftReply.widthAnchor.constraint(equalToConstant: 3).isActive = true
  6271. leftReply.layer.cornerRadius = 5
  6272. leftReply.clipsToBounds = true
  6273. leftReply.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMinXMinYCorner]
  6274. let titleReply = UILabel()
  6275. containerReply.addSubview(titleReply)
  6276. titleReply.translatesAutoresizingMaskIntoConstraints = false
  6277. titleReply.leadingAnchor.constraint(equalTo: leftReply.leadingAnchor, constant: 10).isActive = true
  6278. titleReply.topAnchor.constraint(equalTo: containerReply.topAnchor, constant: 10).isActive = true
  6279. titleReply.trailingAnchor.constraint(lessThanOrEqualTo: containerReply.trailingAnchor, constant: -20).isActive = true
  6280. titleReply.font = UIFont.systemFont(ofSize: 12).bold
  6281. if (data["f_pin"] as? String == idMe) {
  6282. titleReply.text = "You".localized()
  6283. if dataMessages[indexPath.row]["f_pin"] as? String == idMe {
  6284. titleReply.textColor = .white
  6285. leftReply.backgroundColor = .white
  6286. } else {
  6287. titleReply.textColor = .mainColor
  6288. leftReply.backgroundColor = .mainColor
  6289. }
  6290. } else {
  6291. if isContactCenter {
  6292. let user: [User] = users.filter({$0.pin == data["f_pin"] as? String})
  6293. titleReply.text = user.first!.fullName
  6294. } else {
  6295. titleReply.text = self.dataPerson["name"]!!
  6296. }
  6297. if dataMessages[indexPath.row]["f_pin"] as? String == idMe {
  6298. titleReply.textColor = .white
  6299. leftReply.backgroundColor = .white
  6300. } else {
  6301. titleReply.textColor = .mainColor
  6302. leftReply.backgroundColor = .mainColor
  6303. }
  6304. }
  6305. let contentReply = UILabel()
  6306. containerReply.addSubview(contentReply)
  6307. contentReply.translatesAutoresizingMaskIntoConstraints = false
  6308. contentReply.leadingAnchor.constraint(equalTo: leftReply.leadingAnchor, constant: 10).isActive = true
  6309. contentReply.bottomAnchor.constraint(equalTo: containerReply.bottomAnchor, constant: -10).isActive = true
  6310. contentReply.font = UIFont.systemFont(ofSize: 10)
  6311. let message_text = data["message_text"] as? String ?? ""
  6312. let attachment_flag = data["attachment_flag"] as? String ?? ""
  6313. let thumb_chat = data["thumb_id"] as? String ?? ""
  6314. let image_chat = data["image_id"] as? String ?? ""
  6315. let video_chat = data["video_id"] as? String ?? ""
  6316. let file_chat = data["file_id"] as? String ?? ""
  6317. if (attachment_flag == "0" && thumb_chat == "") {
  6318. contentReply.trailingAnchor.constraint(equalTo: containerReply.trailingAnchor, constant: -20).isActive = true
  6319. contentReply.attributedText = message_text.richText()
  6320. } else if (attachment_flag == "1" || image_chat != "") {
  6321. if (message_text == "") {
  6322. contentReply.text = "📷 Photo".localized()
  6323. } else {
  6324. contentReply.attributedText = message_text.richText()
  6325. }
  6326. } else if (attachment_flag == "2" || video_chat != "") {
  6327. if (message_text == "") {
  6328. contentReply.text = "📹 Video".localized()
  6329. } else {
  6330. contentReply.attributedText = message_text.richText()
  6331. }
  6332. } else if (attachment_flag == "6" || file_chat != ""){
  6333. contentReply.trailingAnchor.constraint(equalTo: containerReply.trailingAnchor, constant: -20).isActive = true
  6334. contentReply.text = "📄 \(message_text.components(separatedBy: "|")[0])"
  6335. } else if (attachment_flag == "11") {
  6336. contentReply.text = "❤️ Sticker"
  6337. }
  6338. contentReply.textColor = .white.withAlphaComponent(0.8)
  6339. if (attachment_flag == "1" || attachment_flag == "2" || image_chat != "" || video_chat != "") {
  6340. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  6341. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  6342. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  6343. if let dirPath = paths.first {
  6344. let thumbURL = URL(fileURLWithPath: dirPath).appendingPathComponent(thumb_chat)
  6345. DispatchQueue.main.async {
  6346. let image : UIImage? = {
  6347. if let img = Nexilis.imageCache.object(forKey: thumbChat as NSString) {
  6348. return img
  6349. }
  6350. else if let img = UIImage(contentsOfFile: thumbURL.path)?.resize(target: CGSize(width: 500, height: 500)) {
  6351. Nexilis.imageCache.setObject(img, forKey: thumbChat as NSString)
  6352. return img
  6353. }
  6354. return nil
  6355. }()
  6356. // let image = UIGraphicsRenderer.renderImageAt(url: thumbURL as NSURL, size: CGSize(width: 250, height: 250))
  6357. let imageThumb = UIImageView(image: image)
  6358. containerReply.addSubview(imageThumb)
  6359. imageThumb.layer.cornerRadius = 2.0
  6360. imageThumb.clipsToBounds = true
  6361. imageThumb.contentMode = .scaleAspectFill
  6362. imageThumb.translatesAutoresizingMaskIntoConstraints = false
  6363. imageThumb.trailingAnchor.constraint(equalTo: containerReply.trailingAnchor, constant: -10).isActive = true
  6364. imageThumb.centerYAnchor.constraint(equalTo: containerReply.centerYAnchor).isActive = true
  6365. imageThumb.widthAnchor.constraint(equalToConstant: 30).isActive = true
  6366. imageThumb.heightAnchor.constraint(equalToConstant: 30).isActive = true
  6367. if (attachment_flag == "2") {
  6368. let imagePlay = UIImageView(image: UIImage(systemName: "play.circle.fill"))
  6369. imageThumb.addSubview(imagePlay)
  6370. imagePlay.clipsToBounds = true
  6371. imagePlay.translatesAutoresizingMaskIntoConstraints = false
  6372. imagePlay.centerYAnchor.constraint(equalTo: imageThumb.centerYAnchor).isActive = true
  6373. imagePlay.centerXAnchor.constraint(equalTo: imageThumb.centerXAnchor).isActive = true
  6374. imagePlay.widthAnchor.constraint(equalToConstant: 10).isActive = true
  6375. imagePlay.heightAnchor.constraint(equalToConstant: 10).isActive = true
  6376. imagePlay.tintColor = .white
  6377. }
  6378. titleReply.trailingAnchor.constraint(equalTo: imageThumb.leadingAnchor, constant: -20).isActive = true
  6379. contentReply.trailingAnchor.constraint(equalTo: imageThumb.leadingAnchor, constant: -20).isActive = true
  6380. }
  6381. }
  6382. }
  6383. if (attachment_flag == "11" && message_text.components(separatedBy: "/").count > 1) {
  6384. let imageSticker = UIImageView(image: UIImage(named: (message_text.components(separatedBy: "/")[1]), in: Bundle.resourceBundle(for: Nexilis.self), with: nil))
  6385. containerReply.addSubview(imageSticker)
  6386. imageSticker.layer.cornerRadius = 2.0
  6387. imageSticker.clipsToBounds = true
  6388. imageSticker.translatesAutoresizingMaskIntoConstraints = false
  6389. imageSticker.trailingAnchor.constraint(equalTo: containerReply.trailingAnchor, constant: -10).isActive = true
  6390. imageSticker.centerYAnchor.constraint(equalTo: containerReply.centerYAnchor).isActive = true
  6391. imageSticker.widthAnchor.constraint(equalToConstant: 30).isActive = true
  6392. imageSticker.heightAnchor.constraint(equalToConstant: 30).isActive = true
  6393. titleReply.trailingAnchor.constraint(equalTo: imageSticker.leadingAnchor, constant: -20).isActive = true
  6394. contentReply.trailingAnchor.constraint(equalTo: imageSticker.leadingAnchor, constant: -20).isActive = true
  6395. }
  6396. if !copySession && !forwardSession && !deleteSession {
  6397. let objectTap = ObjectGesture(target: self, action: #selector(contentMessageTapped(_:)))
  6398. containerReply.addGestureRecognizer(objectTap)
  6399. objectTap.indexPath = indexPath
  6400. objectTap.message_id = data["message_id"] as? String ?? ""
  6401. }
  6402. }
  6403. }
  6404. if dataMessages[indexPath.row][TypeDataMessage.is_forwarded] != nil && dataMessages[indexPath.row][TypeDataMessage.is_forwarded] as! Int != 0 && !isLoadingShowLink {
  6405. showForwardedSign()
  6406. }
  6407. func showForwardedSign() {
  6408. topMarginText.constant = topMarginText.constant + 20
  6409. let containerForwarded = UIView()
  6410. containerMessage.addSubview(containerForwarded)
  6411. containerForwarded.translatesAutoresizingMaskIntoConstraints = false
  6412. containerForwarded.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
  6413. containerForwarded.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 15).isActive = true
  6414. containerForwarded.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  6415. containerForwarded.heightAnchor.constraint(equalToConstant: 20).isActive = true
  6416. if thumbChat != "" && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as? String ?? "" != "1") {
  6417. containerForwarded.bottomAnchor.constraint(equalTo: imageThumb.topAnchor, constant: -5).isActive = true
  6418. } else if fileChat != "" && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as? String ?? "" != "1") {
  6419. containerForwarded.bottomAnchor.constraint(equalTo: containerViewFile.topAnchor, constant: -5).isActive = true
  6420. } else if containerMessage.subviews.contains(containerLinkMessage) {
  6421. containerForwarded.bottomAnchor.constraint(equalTo: containerLinkMessage.topAnchor, constant: -5).isActive = true
  6422. } else if dataMessages[indexPath.row]["attachment_flag"] as? String == "11" && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as? String ?? "" != "1") {
  6423. containerForwarded.bottomAnchor.constraint(equalTo: imageSticker.topAnchor, constant: -5).isActive = true
  6424. }
  6425. let imageForwarded = UIImageView()
  6426. containerForwarded.addSubview(imageForwarded)
  6427. imageForwarded.anchor(top: containerForwarded.topAnchor, left: containerForwarded.leftAnchor, width: 15, height: 15)
  6428. imageForwarded.image = UIImage(systemName: "arrowshape.turn.up.right.fill")
  6429. imageForwarded.tintColor = .gray
  6430. let titleForwarded = UILabel()
  6431. containerForwarded.addSubview(titleForwarded)
  6432. titleForwarded.anchor(top: containerForwarded.topAnchor, left: imageForwarded.rightAnchor, right: containerForwarded.rightAnchor, height: 15)
  6433. titleForwarded.font = .systemFont(ofSize: 15)
  6434. let textForwarded = "Forwarded".localized()
  6435. titleForwarded.attributedText = " $\(textForwarded)$".richText()
  6436. }
  6437. topMarginText.isActive = true
  6438. // let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(panGestureCellAction))
  6439. // panGestureRecognizer.delegate = self
  6440. // cellMessage.addGestureRecognizer(panGestureRecognizer)
  6441. return cell
  6442. }
  6443. @objc func imageGroupingTapped(_ sender: ObjectGesture) {
  6444. let listGroupingImages = ListGroupImages()
  6445. listGroupingImages.imageTapped = sender.indexImageTapped
  6446. listGroupingImages.listGroupingImages = sender.listImageFromGrouping
  6447. listGroupingImages.titleName = titleText
  6448. listGroupingImages.isInitiator = sender.isInitiator
  6449. listGroupingImages.updateEditor = { [self] updatedData, replyData, isUpdateDelete in
  6450. if replyData.count == 0 {
  6451. if updatedData.count != 0 && !isUpdateDelete {
  6452. groupImages[sender.listImageFromGrouping[0].messageId] = updatedData
  6453. } else if updatedData.count > 0 {
  6454. let deletedForEveryoneData = updatedData.filter({ $0.dataMessage["lock"] as? String == "1" })
  6455. if deletedForEveryoneData.count != 0 {
  6456. if groupImages[sender.listImageFromGrouping[0].messageId] != nil {
  6457. var dataWillEmpty = updatedData
  6458. while dataWillEmpty.count > 0 {
  6459. if let lastIdx = dataWillEmpty.lastIndex(where: { $0.dataMessage["lock"] as? String == "1" }) {
  6460. if let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String ?? "" == sender.listImageFromGrouping[0].messageId }) {
  6461. if dataWillEmpty[lastIdx].messageId == sender.listImageFromGrouping[0].messageId {
  6462. self.dataMessages.remove(at: idx)
  6463. self.dataMessages.insert(dataWillEmpty[lastIdx].dataMessage, at: idx)
  6464. } else {
  6465. self.dataMessages.insert(dataWillEmpty[lastIdx].dataMessage, at: idx + 1)
  6466. }
  6467. let subData = Array(updatedData[lastIdx+1..<dataWillEmpty.count])
  6468. if subData.count >= 4 {
  6469. groupImages[subData[0].messageId] = subData
  6470. self.dataMessages.insert(subData[0].dataMessage, at: lastIdx + 1)
  6471. } else {
  6472. if subData.count > 0 {
  6473. self.dataMessages.insert(contentsOf: subData.map({ $0.dataMessage }), at: idx + (dataWillEmpty[lastIdx].messageId == sender.listImageFromGrouping[0].messageId ? 1 : 2))
  6474. }
  6475. }
  6476. }
  6477. dataWillEmpty.removeSubrange(lastIdx..<dataWillEmpty.count)
  6478. } else if dataWillEmpty.count >= 4 {
  6479. groupImages[dataWillEmpty[0].messageId] = dataWillEmpty
  6480. dataWillEmpty.removeAll()
  6481. } else {
  6482. if let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String ?? "" == sender.listImageFromGrouping[0].messageId }) {
  6483. self.dataMessages.remove(at: idx)
  6484. self.dataMessages.insert(contentsOf: dataWillEmpty.map({ $0.dataMessage }), at: idx)
  6485. groupImages.removeValue(forKey: sender.listImageFromGrouping[0].messageId)
  6486. }
  6487. dataWillEmpty.removeAll()
  6488. }
  6489. }
  6490. } else {
  6491. }
  6492. } else {
  6493. if updatedData.count >= 4 {
  6494. if updatedData[0].messageId == sender.listImageFromGrouping[0].messageId {
  6495. groupImages[sender.listImageFromGrouping[0].messageId] = updatedData
  6496. } else {
  6497. if let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String ?? "" == sender.listImageFromGrouping[0].messageId }) {
  6498. self.dataMessages.remove(at: idx)
  6499. self.dataMessages.insert(updatedData[0].dataMessage, at: idx)
  6500. groupImages.removeValue(forKey: sender.listImageFromGrouping[0].messageId)
  6501. groupImages[updatedData[0].messageId] = updatedData
  6502. }
  6503. }
  6504. } else {
  6505. if let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String ?? "" == sender.listImageFromGrouping[0].messageId }) {
  6506. groupImages.removeValue(forKey: sender.listImageFromGrouping[0].messageId)
  6507. self.dataMessages.remove(at: idx)
  6508. let dataMessageInGrouping = updatedData.map({ $0.dataMessage })
  6509. self.dataMessages.insert(contentsOf: dataMessageInGrouping, at: idx)
  6510. }
  6511. }
  6512. }
  6513. } else {
  6514. groupImages.removeValue(forKey: sender.listImageFromGrouping[0].messageId)
  6515. if let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String ?? "" == sender.listImageFromGrouping[0].messageId }) {
  6516. self.dataMessages.remove(at: idx)
  6517. }
  6518. }
  6519. DispatchQueue.main.async { [self] in
  6520. tableChatView.reloadData()
  6521. }
  6522. } else if replyData.count != 0 {
  6523. handleReply(indexPath: IndexPath(row: 0, section: 0), dataMessagesImage: replyData)
  6524. }
  6525. }
  6526. self.navigationController?.pushViewController(listGroupingImages, animated: true)
  6527. }
  6528. @objc func tapAck(_ sender: ObjectGesture) {
  6529. if blocking == "1" {
  6530. self.view.makeToast("You blocked this user".localized(), duration: 3)
  6531. return
  6532. }
  6533. if blocking == "-1" {
  6534. self.view.makeToast("You have been blocked by this user".localized(), duration: 3)
  6535. return
  6536. }
  6537. let indexPath = sender.indexPath
  6538. let dataMessages = self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == dataDates[indexPath.section]})
  6539. if dataMessages[indexPath.row]["status"] as? String ?? "" == "8" {
  6540. return
  6541. }
  6542. if !CheckConnection.isConnectedToNetwork() || API.nGetCLXConnState() == 0 {
  6543. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  6544. imageView.tintColor = .white
  6545. let banner = FloatingNotificationBanner(title: "Check your connection".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
  6546. banner.show()
  6547. return
  6548. }
  6549. DispatchQueue.global().async {
  6550. let result = Nexilis.write(message: CoreMessage_TMessageBank.getAckLocationMessage(f_pin: dataMessages[indexPath.row]["f_pin"] as? String ?? "", message_id: dataMessages[indexPath.row]["message_id"] as? String ?? "", l_pin: dataMessages[indexPath.row]["l_pin"] as? String ?? "", server_date: "\(Date().currentTimeMillis())", message_scope_id: dataMessages[indexPath.row]["message_scope_id"] as? String ?? "", longitude: self.longitude, latitude: self.latitude, description: ""))
  6551. if result != nil {
  6552. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  6553. do {
  6554. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  6555. "status" : "8"
  6556. ], _where: "message_id = '\(dataMessages[indexPath.row]["message_id"] as? String ?? "")'")
  6557. } catch {
  6558. rollback.pointee = true
  6559. print("Access database error: \(error.localizedDescription)")
  6560. }
  6561. })
  6562. DispatchQueue.main.async {
  6563. if let index = self.dataMessages.firstIndex(where: {$0["message_id"] as? String == dataMessages[indexPath.row]["message_id"] as? String}) {
  6564. self.dataMessages[index]["status"] = "8"
  6565. let section = self.dataDates.firstIndex(of: self.dataMessages[index]["chat_date"] as? String ?? "")
  6566. let row = self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == self.dataDates[section!]}).firstIndex(where: { $0["message_id"] as? String ?? "" == self.dataMessages[index]["message_id"] as? String ?? ""})
  6567. if row != nil && section != nil {
  6568. self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
  6569. }
  6570. self.view.makeToast("Confirmation Success.".localized(), duration: 3)
  6571. }
  6572. }
  6573. }
  6574. }
  6575. }
  6576. // public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
  6577. // let velocity : CGPoint = gestureRecognizer.location(in: tableChatView)
  6578. // if velocity.x < 0 {
  6579. // return false
  6580. // }
  6581. // return abs(Float(velocity.x)) > abs(Float(velocity.y))
  6582. // }
  6583. //
  6584. // @objc func panGestureCellAction(recognizer: UIPanGestureRecognizer) {
  6585. // let translation = recognizer.translation(in: tableChatView)
  6586. // let x = recognizer.view?.frame.origin.x ?? 0
  6587. // if x >= -(recognizer.view?.frame.size.width ?? 0) * 0.05 {
  6588. // recognizer.view?.center = CGPoint(
  6589. // x: (recognizer.view?.center.x ?? 0) + translation.x,
  6590. // y: (recognizer.view?.center.y ?? 0))
  6591. // recognizer.setTranslation(CGPoint(x: 0, y: 0), in: view)
  6592. // if (recognizer.view?.frame.origin.x ?? 0) > UIScreen.main.bounds.size.width * 0.9 {
  6593. // UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseOut, animations: {
  6594. // recognizer.view?.frame = CGRect(x: 0, y: recognizer.view?.frame.origin.y ?? 0, width: recognizer.view?.frame.size.width ?? 0, height: recognizer.view?.frame.size.height ?? 0)
  6595. // })
  6596. // }
  6597. // }
  6598. // if x <= -(recognizer.view?.frame.size.width ?? 0) * 0.05 {
  6599. // let idMe = User.getMyPin() as String?
  6600. // let indexPath = self.tableChatView.indexPath(for: recognizer.view! as! UITableViewCell)
  6601. // let dataMessages = self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == dataDates[indexPath!.section]})
  6602. // if (dataMessages[indexPath!.row]["f_pin"] as? String == idMe) {
  6603. // let messageInfoVC = MessageInfo()
  6604. // messageInfoVC.data = dataMessages[indexPath!.row]
  6605. // self.navigationController?.pushViewController(messageInfoVC, animated: true)
  6606. // return
  6607. // }
  6608. // }
  6609. // if x >= ((recognizer.view?.frame.size.width ?? 0) * 0.2) {
  6610. // if !hapticSwipeLeft {
  6611. // UINotificationFeedbackGenerator().notificationOccurred(.success)
  6612. // }
  6613. // hapticSwipeLeft = true
  6614. // } else if x < ((recognizer.view?.frame.size.width ?? 0) * 0.2) {
  6615. // hapticSwipeLeft = false
  6616. // }
  6617. // if recognizer.state == .ended {
  6618. // UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseOut) {
  6619. // recognizer.view?.frame = CGRect(x: 0, y: recognizer.view?.frame.origin.y ?? 0, width: recognizer.view?.frame.size.width ?? 0, height: recognizer.view?.frame.size.height ?? 0)
  6620. // } completion: { (finished) in
  6621. // if x > ((recognizer.view?.frame.size.width ?? 0) * 0.2) {
  6622. // self.hapticSwipeLeft = false
  6623. //
  6624. // }
  6625. // }
  6626. // }
  6627. // }
  6628. public func numberOfSections(in tableView: UITableView) -> Int {
  6629. dataDates.count
  6630. }
  6631. public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  6632. let count = dataMessages.filter({ $0["chat_date"] as? String ?? "" == dataDates[section] }).count
  6633. return count
  6634. }
  6635. @objc func contentMessageTapped(_ sender: ObjectGesture) {
  6636. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  6637. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  6638. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  6639. if (sender.image_id != "") {
  6640. if let dirPath = paths.first {
  6641. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.image_id)
  6642. if FileManager.default.fileExists(atPath: imageURL.path) {
  6643. let image = UIImage(contentsOfFile: imageURL.path)
  6644. let previewImageVC = PreviewAttachmentImageVideo(nibName: "PreviewAttachmentImageVideo", bundle: Bundle.resourceBundle(for: Nexilis.self))
  6645. previewImageVC.image = image
  6646. previewImageVC.isHiddenTextField = true
  6647. previewImageVC.modalPresentationStyle = .custom
  6648. previewImageVC.modalTransitionStyle = .crossDissolve
  6649. self.present(previewImageVC, animated: true, completion: nil)
  6650. } else if FileEncryption.shared.isSecureExists(filename: sender.image_id) {
  6651. do {
  6652. let data = try FileEncryption.shared.readSecure(filename: sender.image_id)
  6653. let image = UIImage(data: data!)
  6654. let previewImageVC = PreviewAttachmentImageVideo(nibName: "PreviewAttachmentImageVideo", bundle: Bundle.resourceBundle(for: Nexilis.self))
  6655. previewImageVC.image = image
  6656. previewImageVC.isHiddenTextField = true
  6657. previewImageVC.modalPresentationStyle = .custom
  6658. previewImageVC.modalTransitionStyle = .crossDissolve
  6659. self.present(previewImageVC, animated: true, completion: nil)
  6660. }
  6661. catch {
  6662. print("Error reading secure file")
  6663. }
  6664. } else {
  6665. for view in sender.imageView.subviews {
  6666. if view is UIImageView {
  6667. view.removeFromSuperview()
  6668. }
  6669. }
  6670. let activityIndicator = UIActivityIndicatorView(style: .large)
  6671. activityIndicator.color = .mainColor
  6672. activityIndicator.hidesWhenStopped = true
  6673. activityIndicator.center = CGPoint(x:sender.imageView.frame.width/2,
  6674. y: sender.imageView.frame.height/2)
  6675. activityIndicator.startAnimating()
  6676. sender.imageView.addSubview(activityIndicator)
  6677. Download().startHTTP(forKey: sender.image_id) { (name, progress) in
  6678. guard progress == 100 else {
  6679. return
  6680. }
  6681. do {
  6682. let secureName = try FileEncryption.shared.writeSecure(filename: name)?[0] as? String ?? ""
  6683. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  6684. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  6685. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  6686. if let dirPath = paths.first {
  6687. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.image_id)
  6688. if FileManager.default.fileExists(atPath: imageURL.path) {
  6689. let image = UIImage(contentsOfFile: imageURL.path)
  6690. let save: Bool = SecureUserDefaults.shared.value(forKey: "saveToGallery") ?? false
  6691. if save {
  6692. UIImageWriteToSavedPhotosAlbum(image!, nil, nil, nil)
  6693. }
  6694. }
  6695. else if FileEncryption.shared.isSecureExists(filename: secureName) {
  6696. if let secureData = try FileEncryption.shared.readSecure(filename: secureName) {
  6697. let image = UIImage(data: secureData)
  6698. let save: Bool = SecureUserDefaults.shared.value(forKey: "saveToGallery") ?? false
  6699. if save {
  6700. UIImageWriteToSavedPhotosAlbum(image!, nil, nil, nil)
  6701. }
  6702. }
  6703. }
  6704. }
  6705. } catch {
  6706. }
  6707. DispatchQueue.main.async {
  6708. activityIndicator.stopAnimating()
  6709. self.tableChatView.reloadRows(at: [sender.indexPath], with: .none)
  6710. }
  6711. }
  6712. }
  6713. }
  6714. } else if (sender.gif_id != "") {
  6715. if let dirPath = paths.first {
  6716. let gifURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.gif_id)
  6717. if FileManager.default.fileExists(atPath: gifURL.path) {
  6718. do {
  6719. let data = try Data(contentsOf: gifURL)
  6720. APIS.openImageNexilis(image: UIImage(), data: data, isGIF: true)
  6721. } catch {
  6722. }
  6723. } else if FileEncryption.shared.isSecureExists(filename: sender.gif_id) {
  6724. do {
  6725. if let secureData = try FileEncryption.shared.readSecure(filename: sender.gif_id) {
  6726. APIS.openImageNexilis(image: UIImage(), data: secureData, isGIF: true)
  6727. }
  6728. } catch {
  6729. }
  6730. }
  6731. }
  6732. } else if (sender.video_id != "") {
  6733. if let dirPath = paths.first {
  6734. let videoURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.video_id)
  6735. if FileManager.default.fileExists(atPath: videoURL.path) {
  6736. let player = AVPlayer(url: videoURL as URL)
  6737. let playerVC = AVPlayerViewController()
  6738. playerVC.modalPresentationStyle = .custom
  6739. playerVC.player = player
  6740. self.present(playerVC, animated: true, completion: nil)
  6741. } else if FileEncryption.shared.isSecureExists(filename: sender.video_id) {
  6742. do {
  6743. if let secureData = try FileEncryption.shared.readSecure(filename: sender.video_id) {
  6744. let cachesDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
  6745. let tempPath = cachesDirectory.appendingPathComponent(sender.video_id)
  6746. try secureData.write(to: tempPath)
  6747. let player = AVPlayer(url: tempPath as URL)
  6748. let playerVC = AVPlayerViewController()
  6749. playerVC.modalPresentationStyle = .custom
  6750. playerVC.player = player
  6751. self.present(playerVC, animated: true, completion: nil)
  6752. }
  6753. } catch {
  6754. }
  6755. } else {
  6756. for view in sender.imageView.subviews {
  6757. if view is UIImageView {
  6758. view.removeFromSuperview()
  6759. }
  6760. }
  6761. let container = UIView()
  6762. sender.imageView.addSubview(container)
  6763. container.translatesAutoresizingMaskIntoConstraints = false
  6764. container.centerXAnchor.constraint(equalTo: sender.imageView.centerXAnchor).isActive = true
  6765. container.centerYAnchor.constraint(equalTo: sender.imageView.centerYAnchor).isActive = true
  6766. container.widthAnchor.constraint(equalToConstant: 50).isActive = true
  6767. container.heightAnchor.constraint(equalToConstant: 50).isActive = true
  6768. let circlePath = UIBezierPath(arcCenter: CGPoint(x: 25, y: 25), radius: 20, startAngle: -(.pi / 2), endAngle: .pi * 2, clockwise: true)
  6769. let trackShape = CAShapeLayer()
  6770. trackShape.path = circlePath.cgPath
  6771. trackShape.fillColor = UIColor.clear.cgColor
  6772. trackShape.lineWidth = 10
  6773. trackShape.strokeColor = UIColor.blueBubbleColor.withAlphaComponent(0.3).cgColor
  6774. container.backgroundColor = .clear
  6775. container.layer.addSublayer(trackShape)
  6776. let shapeLoading = CAShapeLayer()
  6777. shapeLoading.path = circlePath.cgPath
  6778. shapeLoading.fillColor = UIColor.clear.cgColor
  6779. shapeLoading.lineWidth = 10
  6780. shapeLoading.strokeEnd = 0
  6781. shapeLoading.strokeColor = UIColor.blueBubbleColor.cgColor
  6782. container.layer.addSublayer(shapeLoading)
  6783. let imageDownload = UIImageView(image: UIImage(systemName: "arrow.down", withConfiguration: UIImage.SymbolConfiguration(pointSize: 10, weight: .bold, scale: .default)))
  6784. imageDownload.tintColor = .white
  6785. container.addSubview(imageDownload)
  6786. imageDownload.translatesAutoresizingMaskIntoConstraints = false
  6787. imageDownload.centerXAnchor.constraint(equalTo: sender.imageView.centerXAnchor).isActive = true
  6788. imageDownload.centerYAnchor.constraint(equalTo: sender.imageView.centerYAnchor).isActive = true
  6789. imageDownload.widthAnchor.constraint(equalToConstant: 30).isActive = true
  6790. imageDownload.heightAnchor.constraint(equalToConstant: 30).isActive = true
  6791. Download().startHTTP(forKey: sender.video_id, isImage: false) { (name, progress) in
  6792. DispatchQueue.main.async {
  6793. guard progress == 100 else {
  6794. shapeLoading.strokeEnd = CGFloat(progress / 100)
  6795. return
  6796. }
  6797. do {
  6798. if let secureName = try FileEncryption.shared.writeSecure(filename: name)?[0] as? String {
  6799. let secureData = try FileEncryption.shared.readSecure(filename: secureName)
  6800. let cachesDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
  6801. let tempPath = cachesDirectory.appendingPathComponent(name)
  6802. try secureData!.write(to: tempPath)
  6803. let save: Bool = SecureUserDefaults.shared.value(forKey: "saveToGallery") ?? false
  6804. if save {
  6805. PHPhotoLibrary.shared().performChanges({
  6806. PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: tempPath)
  6807. }) { saved, error in
  6808. }
  6809. }
  6810. } else {
  6811. let save: Bool = SecureUserDefaults.shared.value(forKey: "saveToGallery") ?? false
  6812. if save {
  6813. let videoURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.video_id)
  6814. PHPhotoLibrary.shared().performChanges({
  6815. PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: videoURL)
  6816. }) { saved, error in
  6817. }
  6818. }
  6819. }
  6820. } catch {
  6821. }
  6822. let idx = self.dataMessages.firstIndex(where: { $0["video_id"] as? String ?? "" == sender.video_id})
  6823. if idx != nil {
  6824. self.dataMessages[idx!]["progress"] = progress
  6825. self.tableChatView.reloadRows(at: [sender.indexPath], with: .none)
  6826. }
  6827. }
  6828. }
  6829. }
  6830. }
  6831. } else if (sender.file_id != "") {
  6832. if let dirPath = paths.first {
  6833. let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.file_id)
  6834. print("MASUK SINI KAH? \(fileURL)")
  6835. if FileManager.default.fileExists(atPath: fileURL.path) {
  6836. self.previewItem = fileURL as NSURL
  6837. let previewController = QLPreviewController()
  6838. let rightBarButton = UIBarButtonItem()
  6839. previewController.navigationItem.rightBarButtonItem = rightBarButton
  6840. previewController.dataSource = self
  6841. previewController.modalPresentationStyle = .custom
  6842. self.present(previewController, animated: true)
  6843. } else if FileEncryption.shared.isSecureExists(filename: sender.file_id) {
  6844. do {
  6845. if let docData = try FileEncryption.shared.readSecure(filename: sender.file_id) {
  6846. let cachesDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
  6847. let tempPath = cachesDirectory.appendingPathComponent(sender.file_id)
  6848. try docData.write(to: tempPath)
  6849. self.previewItem = tempPath as NSURL
  6850. let previewController = QLPreviewController()
  6851. let rightBarButton = UIBarButtonItem()
  6852. previewController.navigationItem.rightBarButtonItem = rightBarButton
  6853. previewController.dataSource = self
  6854. previewController.modalPresentationStyle = .custom
  6855. self.present(previewController,animated: true)
  6856. }
  6857. }
  6858. catch {
  6859. }
  6860. } else {
  6861. for view in sender.containerFile.subviews {
  6862. if !(view is UIImageView) && !(view is UILabel) {
  6863. view.removeFromSuperview()
  6864. }
  6865. }
  6866. let containerLoading = UIView()
  6867. sender.containerFile.addSubview(containerLoading)
  6868. containerLoading.translatesAutoresizingMaskIntoConstraints = false
  6869. containerLoading.centerYAnchor.constraint(equalTo: sender.containerFile.centerYAnchor).isActive = true
  6870. containerLoading.leadingAnchor.constraint(equalTo: sender.labelFile.trailingAnchor, constant: 5).isActive = true
  6871. containerLoading.trailingAnchor.constraint(equalTo: sender.containerFile.trailingAnchor, constant: -5).isActive = true
  6872. containerLoading.widthAnchor.constraint(equalToConstant: 30).isActive = true
  6873. containerLoading.heightAnchor.constraint(equalToConstant: 30).isActive = true
  6874. let circlePath = UIBezierPath(arcCenter: CGPoint(x: 15, y: 15), radius: 10, startAngle: -(.pi / 2), endAngle: .pi * 2, clockwise: true)
  6875. let trackShape = CAShapeLayer()
  6876. trackShape.path = circlePath.cgPath
  6877. trackShape.fillColor = UIColor.clear.cgColor
  6878. trackShape.lineWidth = 5
  6879. trackShape.strokeColor = UIColor.blueBubbleColor.withAlphaComponent(0.3).cgColor
  6880. containerLoading.layer.addSublayer(trackShape)
  6881. let shapeLoading = CAShapeLayer()
  6882. shapeLoading.path = circlePath.cgPath
  6883. shapeLoading.fillColor = UIColor.clear.cgColor
  6884. shapeLoading.lineWidth = 3
  6885. shapeLoading.strokeEnd = 0
  6886. shapeLoading.strokeColor = UIColor.blueBubbleColor.cgColor
  6887. containerLoading.layer.addSublayer(shapeLoading)
  6888. let imageupload = UIImageView(image: UIImage(systemName: "arrow.down", withConfiguration: UIImage.SymbolConfiguration(pointSize: 10, weight: .bold, scale: .default)))
  6889. imageupload.tintColor = .white
  6890. containerLoading.addSubview(imageupload)
  6891. imageupload.translatesAutoresizingMaskIntoConstraints = false
  6892. imageupload.centerYAnchor.constraint(equalTo: containerLoading.centerYAnchor).isActive = true
  6893. imageupload.centerXAnchor.constraint(equalTo: containerLoading.centerXAnchor).isActive = true
  6894. Download().startHTTP(forKey: sender.file_id, isImage: false) { (name, progress) in
  6895. DispatchQueue.main.async {
  6896. guard progress == 100 else {
  6897. shapeLoading.strokeEnd = CGFloat(progress / 100)
  6898. return
  6899. }
  6900. do {
  6901. try FileEncryption.shared.writeSecure(filename: name)
  6902. } catch {
  6903. }
  6904. let idx = self.dataMessages.firstIndex(where: { $0["file_id"] as? String ?? "" == sender.file_id})
  6905. if idx != nil {
  6906. self.dataMessages[idx!]["progress"] = progress
  6907. self.tableChatView.reloadRows(at: [sender.indexPath], with: .none)
  6908. }
  6909. }
  6910. }
  6911. }
  6912. }
  6913. } else if !sender.audio_id.isEmpty {
  6914. if let dirPath = paths.first {
  6915. let audioURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.audio_id)
  6916. if FileManager.default.fileExists(atPath: audioURL.path) {
  6917. do {
  6918. if audioPlayer == nil || audioPlayer?.url != audioURL {
  6919. audioPlayer = try AVAudioPlayer(contentsOf: audioURL)
  6920. audioPlayer?.prepareToPlay()
  6921. audioPlayer?.play()
  6922. } else if audioPlayer!.isPlaying {
  6923. audioPlayer?.pause()
  6924. } else {
  6925. audioPlayer?.play()
  6926. }
  6927. } catch {
  6928. }
  6929. } else if FileEncryption.shared.isSecureExists(filename: sender.audio_id) {
  6930. do {
  6931. if let audioData = try FileEncryption.shared.readSecure(filename: sender.audio_id) {
  6932. let cachesDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
  6933. let tempPath = cachesDirectory.appendingPathComponent(sender.audio_id)
  6934. try audioData.write(to: tempPath)
  6935. if audioPlayer == nil || audioPlayer?.url != tempPath {
  6936. audioPlayer = try AVAudioPlayer(contentsOf: tempPath)
  6937. audioPlayer?.prepareToPlay()
  6938. audioPlayer?.play()
  6939. } else if audioPlayer!.isPlaying {
  6940. audioPlayer?.pause()
  6941. } else {
  6942. audioPlayer?.play()
  6943. }
  6944. }
  6945. } catch {
  6946. }
  6947. }
  6948. }
  6949. } else {
  6950. DispatchQueue.main.async {
  6951. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == sender.message_id})
  6952. if idx == nil {
  6953. return
  6954. }
  6955. let section = self.dataDates.firstIndex(of: self.dataMessages[idx!]["chat_date"] as? String ?? "")
  6956. if section == nil {
  6957. return
  6958. }
  6959. let row = self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == self.dataDates[section!]}).firstIndex(where: { $0["message_id"] as? String == self.dataMessages[idx!]["message_id"] as? String})
  6960. if row == nil {
  6961. return
  6962. }
  6963. let indexPath = IndexPath(row: row!, section: section!)
  6964. self.tableChatView.scrollToRow(at: indexPath, at: .middle, animated: true)
  6965. DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
  6966. if let cell = self.tableChatView.cellForRow(at: indexPath) {
  6967. let containerMessage = cell.contentView.subviews[0]
  6968. let idMe = User.getMyPin() as String?
  6969. if (self.dataMessages[idx!]["f_pin"] as? String == idMe) {
  6970. containerMessage.backgroundColor = .blueBubbleColor.withAlphaComponent(0.3)
  6971. DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
  6972. if (self.dataMessages[idx!]["attachment_flag"] as? String == "11") {
  6973. containerMessage.backgroundColor = .clear
  6974. } else {
  6975. containerMessage.backgroundColor = .blueBubbleColor
  6976. }
  6977. }
  6978. } else {
  6979. containerMessage.backgroundColor = .whiteBubbleColor.withAlphaComponent(0.3)
  6980. DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
  6981. if (self.dataMessages[idx!]["attachment_flag"] as? String == "11") {
  6982. containerMessage.backgroundColor = .clear
  6983. } else {
  6984. containerMessage.backgroundColor = .whiteBubbleColor
  6985. }
  6986. }
  6987. }
  6988. }
  6989. }
  6990. }
  6991. }
  6992. }
  6993. func highlightedText(for text: String, textView: UITextView) -> NSAttributedString {
  6994. let mutableAttributedString = textView.attributedText!.mutableCopy() as! NSMutableAttributedString
  6995. if let range = textView.attributedText.string.range(of: text) {
  6996. let nsRange = NSRange(range, in: textView.attributedText.string)
  6997. mutableAttributedString.addAttribute(.backgroundColor, value: UIColor.lightGray.withAlphaComponent(0.5), range: NSRange(range, in: text))
  6998. }
  6999. return mutableAttributedString
  7000. }
  7001. func removeHighlightedText(for text: String, textView: UITextView) -> NSAttributedString {
  7002. let mutableAttributedString = textView.attributedText!.mutableCopy() as! NSMutableAttributedString
  7003. if let range = textView.attributedText.string.range(of: text) {
  7004. let nsRange = NSRange(range, in: textView.attributedText.string)
  7005. mutableAttributedString.removeAttribute(.backgroundColor, range: NSRange(range, in: text))
  7006. }
  7007. return mutableAttributedString
  7008. }
  7009. @objc func tapMessageText(_ sender: ObjectGesture) {
  7010. var stringURl = sender.message_id
  7011. if stringURl.lowercased().starts(with: "www.") {
  7012. stringURl = "https://" + stringURl.replacingOccurrences(of: "www.", with: "")
  7013. }
  7014. guard let url = URL(string: stringURl) else { return }
  7015. UIApplication.shared.open(url)
  7016. }
  7017. // public func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
  7018. // if copySession || forwardSession || deleteSession {
  7019. // return nil
  7020. // }
  7021. // let idMe = User.getMyPin() as String?
  7022. // if (dataMessages[indexPath.row]["f_pin"] as? String != idMe) {
  7023. // return nil
  7024. // }
  7025. // let messageInfoVC = MessageInfo()
  7026. // messageInfoVC.data = dataMessages[indexPath.row]
  7027. // self.navigationController?.show(messageInfoVC, sender: nil)
  7028. // return UISwipeActionsConfiguration()
  7029. // }
  7030. //
  7031. // public func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
  7032. // if copySession || forwardSession || deleteSession {
  7033. // return nil
  7034. // }
  7035. // let action = UIContextualAction(style: .normal,
  7036. // title: "") { [weak self] (action, view, completionHandler) in
  7037. // self?.handleReply(indexPath: indexPath)
  7038. // completionHandler(true)
  7039. // }
  7040. // action.backgroundColor = .white
  7041. // action.image = UIImage(systemName: "arrowshape.turn.up.left.fill")?.withTintColor(.black, renderingMode: .alwaysOriginal)
  7042. // return UISwipeActionsConfiguration(actions: [action])
  7043. // }
  7044. private func handleReply(indexPath: IndexPath, dataMessagesImage: [String: Any?] = [:], reffId: String = "") {
  7045. var dataMessages = self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == dataDates[indexPath.section]})
  7046. if reffId.isEmpty {
  7047. self.deleteReplyView()
  7048. if dataMessagesImage.count != 0 {
  7049. dataMessages = [dataMessagesImage]
  7050. } else {
  7051. self.textFieldSend.becomeFirstResponder()
  7052. }
  7053. self.reffId = dataMessages[indexPath.row]["message_id"] as? String
  7054. } else {
  7055. dataMessages = self.dataMessages.filter({ $0["message_id"] as? String ?? "" == reffId })
  7056. self.reffId = reffId
  7057. }
  7058. UIView.animate(withDuration: 0.25, delay: 0.0, options: .curveEaseInOut, animations: {
  7059. self.constraintTopTextField.constant = self.constraintTopTextField.constant + 50
  7060. }, completion: nil)
  7061. if (self.currentIndexpath != nil) {
  7062. DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
  7063. self.tableChatView.scrollToRow(at: IndexPath(row: self.currentIndexpath!.row, section: self.currentIndexpath!.section), at: .none, animated: false)
  7064. }
  7065. } else {
  7066. self.tableChatView.scrollToBottom()
  7067. }
  7068. self.viewTextfield.addSubview(self.containerPreviewReply)
  7069. self.containerPreviewReply.translatesAutoresizingMaskIntoConstraints = false
  7070. self.containerPreviewReply.leadingAnchor.constraint(equalTo: self.viewTextfield.leadingAnchor).isActive = true
  7071. self.containerPreviewReply.topAnchor.constraint(equalTo: self.viewTextfield.topAnchor).isActive = true
  7072. if !self.containerLink.isDescendant(of: self.viewTextfield) {
  7073. self.bottomAnchorPreviewReply = self.containerPreviewReply.bottomAnchor.constraint(equalTo: self.textFieldSend.topAnchor)
  7074. } else {
  7075. self.bottomAnchorPreviewReply = self.containerPreviewReply.bottomAnchor.constraint(equalTo: self.containerLink.topAnchor)
  7076. }
  7077. self.bottomAnchorPreviewReply.isActive = true
  7078. self.containerPreviewReply.trailingAnchor.constraint(equalTo: self.viewTextfield.trailingAnchor).isActive = true
  7079. self.containerPreviewReply.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : .secondaryColor
  7080. let leftReply = UIView()
  7081. self.containerPreviewReply.addSubview(leftReply)
  7082. leftReply.translatesAutoresizingMaskIntoConstraints = false
  7083. leftReply.leadingAnchor.constraint(equalTo: self.viewTextfield.leadingAnchor).isActive = true
  7084. leftReply.topAnchor.constraint(equalTo: self.containerPreviewReply.topAnchor).isActive = true
  7085. leftReply.bottomAnchor.constraint(equalTo: self.containerPreviewReply.bottomAnchor).isActive = true
  7086. leftReply.widthAnchor.constraint(equalToConstant: 3).isActive = true
  7087. leftReply.backgroundColor = .orangeColor
  7088. let titleReply = UILabel()
  7089. self.containerPreviewReply.addSubview(titleReply)
  7090. titleReply.translatesAutoresizingMaskIntoConstraints = false
  7091. titleReply.leadingAnchor.constraint(equalTo: leftReply.leadingAnchor, constant: 10).isActive = true
  7092. titleReply.topAnchor.constraint(equalTo: self.containerPreviewReply.topAnchor, constant: 10).isActive = true
  7093. titleReply.font = UIFont.systemFont(ofSize: 12).bold
  7094. let idMe = User.getMyPin() as String?
  7095. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  7096. titleReply.text = "You".localized()
  7097. } else {
  7098. if self.isContactCenter {
  7099. let user: [User] = self.users.filter({$0.pin == dataMessages[indexPath.row]["f_pin"] as? String})
  7100. titleReply.text = user.first!.fullName
  7101. } else {
  7102. titleReply.text = self.dataPerson["name"]!!
  7103. }
  7104. }
  7105. titleReply.textColor = .orangeColor
  7106. let contentReply = UILabel()
  7107. self.containerPreviewReply.addSubview(contentReply)
  7108. contentReply.translatesAutoresizingMaskIntoConstraints = false
  7109. contentReply.leadingAnchor.constraint(equalTo: leftReply.leadingAnchor, constant: 10).isActive = true
  7110. contentReply.topAnchor.constraint(equalTo: titleReply.bottomAnchor).isActive = true
  7111. contentReply.font = UIFont.systemFont(ofSize: 10)
  7112. let message_text = dataMessages[indexPath.row]["message_text"] as? String ?? ""
  7113. let attachment_flag = dataMessages[indexPath.row]["attachment_flag"] as? String ?? ""
  7114. let thumb_chat = dataMessages[indexPath.row]["thumb_id"] as? String ?? ""
  7115. let image_chat = dataMessages[indexPath.row]["image_id"] as? String ?? ""
  7116. let video_chat = dataMessages[indexPath.row]["video_id"] as? String ?? ""
  7117. let file_chat = dataMessages[indexPath.row]["file_id"] as? String ?? ""
  7118. if (attachment_flag == "0" && thumb_chat == "") {
  7119. contentReply.attributedText = message_text.richText()
  7120. } else if (attachment_flag == "1" || image_chat != "") {
  7121. if (message_text == "") {
  7122. contentReply.text = "📷 Photo".localized()
  7123. } else {
  7124. contentReply.attributedText = message_text.richText()
  7125. }
  7126. } else if (attachment_flag == "2" || video_chat != "") {
  7127. if (message_text == "") {
  7128. contentReply.text = "📹 Video".localized()
  7129. } else {
  7130. contentReply.attributedText = message_text.richText()
  7131. }
  7132. } else if (attachment_flag == "6" || file_chat != ""){
  7133. contentReply.text = "📄 \(message_text.components(separatedBy: "|")[0])"
  7134. } else if (attachment_flag == "11") {
  7135. contentReply.text = "❤️ Sticker"
  7136. }
  7137. contentReply.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .gray
  7138. let buttonCancelReply = UIButton(type: .custom)
  7139. self.containerPreviewReply.addSubview(buttonCancelReply)
  7140. buttonCancelReply.translatesAutoresizingMaskIntoConstraints = false
  7141. buttonCancelReply.trailingAnchor.constraint(equalTo: self.containerPreviewReply.trailingAnchor, constant: -10).isActive = true
  7142. buttonCancelReply.centerYAnchor.constraint(equalTo: self.containerPreviewReply.centerYAnchor).isActive = true
  7143. buttonCancelReply.setImage(UIImage(systemName: "xmark.circle" , withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .regular, scale: .default)), for: .normal)
  7144. buttonCancelReply.addTarget(nil, action: #selector(self.deleteReplyView), for: .touchUpInside)
  7145. buttonCancelReply.backgroundColor = .clear
  7146. buttonCancelReply.tintColor = .mainColor
  7147. if (attachment_flag == "1" || attachment_flag == "2" || image_chat != "" || video_chat != "") {
  7148. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  7149. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  7150. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  7151. if let dirPath = paths.first {
  7152. let thumbURL = URL(fileURLWithPath: dirPath).appendingPathComponent(thumb_chat)
  7153. let image : UIImage? = {
  7154. if let img = Nexilis.imageCache.object(forKey: thumb_chat as NSString) {
  7155. return img
  7156. }
  7157. else if let img = UIImage(contentsOfFile: thumbURL.path)?.resize(target: CGSize(width: 500, height: 500)) {
  7158. Nexilis.imageCache.setObject(img, forKey: thumb_chat as NSString)
  7159. return img
  7160. }
  7161. return nil
  7162. }()
  7163. // let image = UIGraphicsRenderer.renderImageAt(url: thumbURL as NSURL, size: CGSize(width: 250, height: 250))
  7164. let imageThumb = UIImageView(image: image)
  7165. self.containerPreviewReply.addSubview(imageThumb)
  7166. imageThumb.layer.cornerRadius = 2.0
  7167. imageThumb.clipsToBounds = true
  7168. imageThumb.translatesAutoresizingMaskIntoConstraints = false
  7169. imageThumb.trailingAnchor.constraint(equalTo: buttonCancelReply.leadingAnchor, constant: -10).isActive = true
  7170. imageThumb.centerYAnchor.constraint(equalTo: self.containerPreviewReply.centerYAnchor).isActive = true
  7171. imageThumb.widthAnchor.constraint(equalToConstant: 30).isActive = true
  7172. imageThumb.heightAnchor.constraint(equalToConstant: 30).isActive = true
  7173. if (attachment_flag == "2") {
  7174. let imagePlay = UIImageView(image: UIImage(systemName: "play.circle.fill"))
  7175. imageThumb.addSubview(imagePlay)
  7176. imagePlay.clipsToBounds = true
  7177. imagePlay.translatesAutoresizingMaskIntoConstraints = false
  7178. imagePlay.centerYAnchor.constraint(equalTo: imageThumb.centerYAnchor).isActive = true
  7179. imagePlay.centerXAnchor.constraint(equalTo: imageThumb.centerXAnchor).isActive = true
  7180. imagePlay.widthAnchor.constraint(equalToConstant: 10).isActive = true
  7181. imagePlay.heightAnchor.constraint(equalToConstant: 10).isActive = true
  7182. imagePlay.tintColor = .white
  7183. }
  7184. }
  7185. }
  7186. if (attachment_flag == "11") {
  7187. let imageSticker = UIImageView(image: UIImage(named: (message_text.components(separatedBy: "/")[1]), in: Bundle.resourceBundle(for: Nexilis.self), with: nil))
  7188. self.containerPreviewReply.addSubview(imageSticker)
  7189. imageSticker.layer.cornerRadius = 2.0
  7190. imageSticker.clipsToBounds = true
  7191. imageSticker.translatesAutoresizingMaskIntoConstraints = false
  7192. imageSticker.trailingAnchor.constraint(equalTo: buttonCancelReply.leadingAnchor, constant: -10).isActive = true
  7193. imageSticker.centerYAnchor.constraint(equalTo: self.containerPreviewReply.centerYAnchor).isActive = true
  7194. imageSticker.widthAnchor.constraint(equalToConstant: 30).isActive = true
  7195. imageSticker.heightAnchor.constraint(equalToConstant: 30).isActive = true
  7196. }
  7197. }
  7198. func scrollToFirstSearchMessage(indexScroll: Int = 1) {
  7199. if textSearch.count < 2 {
  7200. return
  7201. }
  7202. var lastIndex = 0
  7203. let messageTextForSearch: [[String: Any?]] = self.dataMessages.reversed()
  7204. for idx in 0..<messageTextForSearch.count {
  7205. if (messageTextForSearch[idx]["message_text"] as? String ?? "").lowercased().contains(textSearch) {
  7206. lastIndex += 1
  7207. if lastIndex < indexScroll {
  7208. continue
  7209. }
  7210. lastScrollIdxSearch = lastIndex
  7211. let section = self.dataDates.firstIndex(of: messageTextForSearch[idx]["chat_date"] as? String ?? "")
  7212. if section == nil {
  7213. return
  7214. }
  7215. let row = self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == self.dataDates[section!]}).firstIndex(where: { $0["message_id"] as? String == messageTextForSearch[idx]["message_id"] as? String})
  7216. if row == nil {
  7217. return
  7218. }
  7219. let indexPath = IndexPath(row: row!, section: section!)
  7220. self.tableChatView.scrollToRow(at: indexPath, at: .middle, animated: true)
  7221. DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
  7222. if let cell = self.tableChatView.cellForRow(at: indexPath) {
  7223. let containerMessage = cell.contentView.subviews[0]
  7224. let idMe = User.getMyPin() as String?
  7225. if (messageTextForSearch[idx]["f_pin"] as? String == idMe) {
  7226. containerMessage.backgroundColor = .blueBubbleColor.withAlphaComponent(0.3)
  7227. DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
  7228. if (messageTextForSearch[idx]["attachment_flag"] as? String == "11") {
  7229. containerMessage.backgroundColor = .clear
  7230. } else {
  7231. containerMessage.backgroundColor = .blueBubbleColor
  7232. }
  7233. }
  7234. } else {
  7235. containerMessage.backgroundColor = .whiteBubbleColor.withAlphaComponent(0.3)
  7236. DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
  7237. if (messageTextForSearch[idx]["attachment_flag"] as? String == "11") {
  7238. containerMessage.backgroundColor = .clear
  7239. } else {
  7240. containerMessage.backgroundColor = .whiteBubbleColor
  7241. }
  7242. }
  7243. }
  7244. }
  7245. }
  7246. titleSearchMatches.isHidden = false
  7247. if countMatchesSearch != 0 {
  7248. if countMatchesSearch > 1 {
  7249. titleSearchMatches.text = "\(lastScrollIdxSearch) " + "of".localized() + " \(countMatchesSearch) " + "matches".localized()
  7250. } else {
  7251. titleSearchMatches.text = "\(countMatchesSearch) " + "matches".localized()
  7252. }
  7253. } else {
  7254. titleSearchMatches.text = "Not found".localized()
  7255. }
  7256. if lastScrollIdxSearch == countMatchesSearch || countMatchesSearch == 0 {
  7257. buttonUp.isEnabled = false
  7258. buttonUp.tintColor = .gray
  7259. } else {
  7260. buttonUp.isEnabled = true
  7261. buttonUp.tintColor = .mainColor
  7262. }
  7263. if countMatchesSearch == 0 || lastScrollIdxSearch == 1 || countMatchesSearch == 1 {
  7264. buttonDown.isEnabled = false
  7265. buttonDown.tintColor = .gray
  7266. } else {
  7267. buttonDown.isEnabled = true
  7268. buttonDown.tintColor = .mainColor
  7269. }
  7270. break
  7271. }
  7272. }
  7273. }
  7274. public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
  7275. let indexPath = tableChatView.indexPathsForVisibleRows?.first
  7276. if indexPath != nil {
  7277. let headerRect = tableChatView.rectForHeader(inSection: indexPath!.section)
  7278. let isPinned = headerRect.origin.y <= scrollView.contentOffset.y
  7279. if listViewOnSection.count != 0 && listViewOnSection.count - 1 == indexPath!.section && indexPath!.row > 0 {
  7280. let sect = listViewOnSection.count - 1 < currentIndexpath!.section ? listViewOnSection.count - 1 : currentIndexpath!.section
  7281. let headerView = listViewOnSection[sect]
  7282. headerView.isHidden = true
  7283. }
  7284. }
  7285. }
  7286. public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
  7287. if !decelerate {
  7288. let indexPath = tableChatView.indexPathsForVisibleRows?.first
  7289. if indexPath != nil {
  7290. let headerRect = tableChatView.rectForHeader(inSection: indexPath!.section)
  7291. let isPinned = headerRect.origin.y <= scrollView.contentOffset.y
  7292. if listViewOnSection.count != 0 && listViewOnSection.count - 1 == indexPath!.section && isPinned {
  7293. let sect = listViewOnSection.count - 1 < currentIndexpath!.section ? listViewOnSection.count - 1 : currentIndexpath!.section
  7294. let headerView = listViewOnSection[sect]
  7295. headerView.isHidden = true
  7296. }
  7297. }
  7298. }
  7299. }
  7300. }
  7301. extension UITableView {
  7302. func scrollToBottom(isAnimated:Bool = true){
  7303. DispatchQueue.main.async {
  7304. if self.numberOfSections == 0 {
  7305. return
  7306. }
  7307. let indexPath = IndexPath(
  7308. row: self.numberOfRows(inSection: self.numberOfSections-1) - 1,
  7309. section: self.numberOfSections - 1)
  7310. if indexPath.row != -1 {
  7311. self.scrollToRow(at: indexPath, at: .bottom, animated: isAnimated)
  7312. }
  7313. }
  7314. }
  7315. func scrollToTop(isAnimated:Bool = true) {
  7316. DispatchQueue.main.async {
  7317. let indexPath = IndexPath(row: 0, section: 0)
  7318. if indexPath.row != -1 {
  7319. self.scrollToRow(at: indexPath, at: .top, animated: isAnimated)
  7320. }
  7321. }
  7322. }
  7323. }
  7324. extension UIImage {
  7325. public func imageWithInsets(insets: UIEdgeInsets) -> UIImage? {
  7326. UIGraphicsBeginImageContextWithOptions(
  7327. CGSize(width: self.size.width + insets.left + insets.right,
  7328. height: self.size.height + insets.top + insets.bottom), false, self.scale)
  7329. let _ = UIGraphicsGetCurrentContext()
  7330. let origin = CGPoint(x: insets.left, y: insets.top)
  7331. self.draw(at: origin)
  7332. let imageWithInsets = UIGraphicsGetImageFromCurrentImageContext()
  7333. UIGraphicsEndImageContext()
  7334. return imageWithInsets
  7335. }
  7336. }
  7337. extension EditorPersonal: UISearchBarDelegate {
  7338. public func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
  7339. textSearch = searchText.trimmingCharacters(in: .whitespacesAndNewlines)
  7340. countMatchesSearch = 0
  7341. titleSearchMatches.isHidden = true
  7342. tableChatView.reloadData()
  7343. scrollToFirstSearchMessage()
  7344. }
  7345. }
  7346. public class ObjectGesture: UITapGestureRecognizer {
  7347. public var message_id = ""
  7348. public var image_id = ""
  7349. public var video_id = ""
  7350. public var file_id = ""
  7351. public var audio_id = ""
  7352. public var gif_id = ""
  7353. public var imageView = UIImageView()
  7354. public var containerFile = UIView()
  7355. public var labelFile = UILabel()
  7356. public var videoURL: NSURL?
  7357. public var indexPath = IndexPath()
  7358. public var indexImageTapped: Int!
  7359. public var listImageFromGrouping: [ImageGrouping]!
  7360. public var isInitiator: Bool!
  7361. }
  7362. class navigationQLPreviewDocument: UIBarButtonItem {
  7363. var navigation = UINavigationController()
  7364. }
  7365. class segmentedControllerObject: UISegmentedControl {
  7366. var navigation = UINavigationController()
  7367. }
  7368. public class ImageGrouping {
  7369. public var messageId = ""
  7370. public var thumbId = ""
  7371. public var imageId = ""
  7372. public var status = ""
  7373. public var time = ""
  7374. public var lPin = ""
  7375. public var dataMessage: [String: Any?] = [:]
  7376. public var dataPerson: [String: String?] = [:]
  7377. public var dataGroup: [String: Any?] = [:]
  7378. public var dataTopic: [String: Any?] = [:]
  7379. public var isSelected = false
  7380. public init(messageId: String, thumbId: String, imageId: String, status: String, time: String, lPin: String, dataMessage: [String: Any?], dataPerson: [String: String?], dataGroup: [String: Any?], dataTopic: [String: Any?]) {
  7381. self.messageId = messageId
  7382. self.thumbId = thumbId
  7383. self.imageId = imageId
  7384. self.status = status
  7385. self.time = time
  7386. self.lPin = lPin
  7387. self.dataMessage = dataMessage
  7388. self.dataPerson = dataPerson
  7389. self.dataGroup = dataGroup
  7390. self.dataTopic = dataTopic
  7391. }
  7392. }
  7393. public class TypeDataMessage {
  7394. public static let message_id = "message_id"
  7395. public static let f_pin = "f_pin"
  7396. public static let l_pin = "l_pin"
  7397. public static let message_scope_id = "message_scope_id"
  7398. public static let server_date = "server_date"
  7399. public static let status = "status"
  7400. public static let message_text = "message_text"
  7401. public static let audio_id = "audio_id"
  7402. public static let video_id = "video_id"
  7403. public static let image_id = "image_id"
  7404. public static let thumb_id = "thumb_id"
  7405. public static let read_receipts = "read_receipts"
  7406. public static let chat_id = "chat_id"
  7407. public static let file_id = "file_id"
  7408. public static let attachment_flag = "attachment_flag"
  7409. public static let reff_id = "reff_id"
  7410. public static let lock = "lock"
  7411. public static let is_stared = "is_stared"
  7412. public static let blog_id = "blog_id"
  7413. public static let credential = "credential"
  7414. public static let progress = "progress"
  7415. public static let chat_date = "chat_date"
  7416. public static let is_call_center = "is_call_center"
  7417. public static let call_center_id = "call_center_id"
  7418. public static let opposite_pin = "opposite_pin"
  7419. public static let last_edit = "last_edit"
  7420. public static let gif_id = "gif_id"
  7421. public static let is_forwarded = "is_forwarded"
  7422. public static let is_secret = "is_secret"
  7423. }