EditorPersonal.swift 358 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993
  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. public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestureRecognizerDelegate {
  16. @IBOutlet var viewButton: UIView!
  17. @IBOutlet var constraintViewTextField: NSLayoutConstraint!
  18. @IBOutlet var buttonVoice: UIButton!
  19. @IBOutlet var buttonSendImage: UIButton!
  20. @IBOutlet var buttonSendPhoto: UIButton!
  21. @IBOutlet var buttonSendSticker: UIButton!
  22. @IBOutlet var buttonSendFile: UIButton!
  23. @IBOutlet var textFieldSend: UITextView!
  24. @IBOutlet var heightTextFieldSend: NSLayoutConstraint!
  25. @IBOutlet var buttonSendChat: UIButton!
  26. @IBOutlet var tableChatView: UITableView!
  27. @IBOutlet var constraintTopTextField: NSLayoutConstraint!
  28. @IBOutlet var constraintBottomAttachment: NSLayoutConstraint!
  29. @IBOutlet var viewTextfield: UIView!
  30. @IBOutlet weak var buttonAckConfidential: UIButton!
  31. @IBOutlet weak var constraintLeftTextField: NSLayoutConstraint!
  32. @IBOutlet weak var constraintBottomTableViewWithTextfield: NSLayoutConstraint!
  33. @IBOutlet weak var viewAttachment: UIStackView!
  34. public var dataPerson: [String: String?] = [:]
  35. var dataMessages: [[String: Any?]] = []
  36. var dataDates: [String] = []
  37. var users: [User] = []
  38. public var dataMessageForward: [[String: Any?]]?
  39. var imageVideoPicker: ImageVideoPicker!
  40. var documentPicker: DocumentPicker!
  41. var currentIndexpath: IndexPath?
  42. var previewItem: NSURL?
  43. var reffId: String?
  44. var stickers = [String]()
  45. public var unique_l_pin = ""
  46. public var isContactCenter = false
  47. public var isRequestContactCenter = true
  48. public var fromNotification = false
  49. public var onGoingCC = false
  50. public var fPinContacCenter = ""
  51. public var complaintId = ""
  52. var channelContactCenter = ""
  53. var counter = 0
  54. var dateStartCC = ""
  55. var markerCounter: String?
  56. var buttonScrollToBottom = UIButton()
  57. let indicatorCounterBSTB = UIView()
  58. let labelCounter = UILabel()
  59. var copySession = false
  60. var forwardSession = false
  61. var deleteSession = false
  62. var isSearching = false
  63. let containerMultpileSelectSession = UIView()
  64. let containerAction = UIView()
  65. var removed = false
  66. var isConfidential = false
  67. var isAck = false
  68. let viewSticker = UIView()
  69. let containerLink = UIView()
  70. let containerPreviewReply = UIView()
  71. var bottomAnchorPreviewReply = NSLayoutConstraint()
  72. var blocking = ""
  73. var timeoutCC = Timer()
  74. var nowSelectedCategoryCC = ""
  75. var showToastTwiceClick = false
  76. var showToast30s = false
  77. var allowTyping = true
  78. var hapticSwipeLeft = false
  79. var listTimerCredential: [String: Int] = [:]
  80. var timerCredential: [String: Timer] = [:]
  81. let contactChatNav = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "contactChatNav") as! UINavigationController
  82. var searchBar: UISearchBar!
  83. var constraintBottomContainerMultpileSelectSession = NSLayoutConstraint()
  84. var titleSearchMatches: UILabel!
  85. var textSearch = ""
  86. var countMatchesSearch = 0
  87. var lastScrollIdxSearch = 0
  88. var buttonUp: UIButton!
  89. var buttonDown: UIButton!
  90. var multipleOffsetUp = 1
  91. var lastOffsetDown = 1
  92. var gettingDataMessage = true
  93. var showingLink = ""
  94. var isAlwaysHideLinkPreview = false
  95. var timerCheckLink: Timer?
  96. var timerLongPressLink: Timer?
  97. var lastTouchPoint: CGPoint = .zero
  98. var isLinkCopied = false
  99. var touchedSubview = UIView()
  100. var listViewOnSection: [UIView] = []
  101. var fromVCAC = false
  102. var serviceIdCC = ""
  103. var isDirectCC = false
  104. public override func viewDidDisappear(_ animated: Bool) {
  105. if self.isMovingFromParent {
  106. for timer in self.timerCredential.values {
  107. timer.invalidate()
  108. }
  109. self.timeoutCC.invalidate()
  110. UserDefaults.standard.removeObject(forKey: "inEditorPersonal")
  111. NotificationCenter.default.removeObserver(self)
  112. }
  113. }
  114. public override func viewDidAppear(_ animated: Bool) {
  115. let navBarAppearance = UINavigationBarAppearance()
  116. navBarAppearance.configureWithOpaqueBackground()
  117. navBarAppearance.backgroundColor = UIColor.mainColor
  118. navigationController?.navigationBar.standardAppearance = navBarAppearance
  119. navigationController?.navigationBar.scrollEdgeAppearance = navBarAppearance
  120. navigationController?.navigationBar.isTranslucent = false
  121. navigationController?.navigationBar.backgroundColor = .mainColor
  122. navigationController?.navigationBar.tintColor = .white
  123. navigationController?.navigationBar.overrideUserInterfaceStyle = .dark
  124. navigationController?.navigationBar.barStyle = .black
  125. if self.navigationController?.isNavigationBarHidden ?? false {
  126. self.navigationController?.setNavigationBarHidden(false, animated: false)
  127. }
  128. updateProfile()
  129. gettingDataMessage = false
  130. let indexPath = tableChatView.indexPathsForVisibleRows?.first
  131. if indexPath != nil {
  132. let headerRect = tableChatView.rectForHeader(inSection: indexPath!.section)
  133. let isPinned = headerRect.origin.y <= tableChatView.contentOffset.y
  134. if listViewOnSection.count != 0 && listViewOnSection.count - 1 <= indexPath!.section && isPinned {
  135. let headerView = listViewOnSection[currentIndexpath!.section]
  136. headerView.isHidden = true
  137. }
  138. }
  139. }
  140. public override func viewDidLoad() {
  141. super.viewDidLoad()
  142. // navigationController?.navigationBar.topItem?.title = ""
  143. viewButton.layer.shadowColor = UIColor.gray.cgColor
  144. viewButton.layer.shadowOpacity = 1
  145. viewButton.layer.shadowOffset = .zero
  146. viewButton.layer.shadowRadius = 3
  147. // buttonVoice.setImage(resizeImage(image: UIImage(named: "Voice-Record", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, targetSize: CGSize(width: 30, height: 30)), for: .normal)
  148. buttonSendImage.setImage(resizeImage(image: UIImage(named: "Send-Image", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, targetSize: CGSize(width: 30, height: 30)).withTintColor(.mainColor), for: .normal)
  149. buttonSendPhoto.setImage(resizeImage(image: UIImage(named: "Camera", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, targetSize: CGSize(width: 30, height: 30)).withTintColor(.mainColor), for: .normal)
  150. buttonSendSticker.setImage(resizeImage(image: UIImage(named: "Sticker---Emoji", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, targetSize: CGSize(width: 30, height: 30)).withTintColor(.mainColor), for: .normal)
  151. buttonSendFile.setImage(resizeImage(image: UIImage(named: "File---Documents", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, targetSize: CGSize(width: 30, height: 30)).withTintColor(.mainColor), for: .normal)
  152. buttonSendChat.setImage(resizeImage(image: UIImage(named: "Send-(White)", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, targetSize: CGSize(width: 30, height: 30)).withRenderingMode(.alwaysOriginal), for: .normal)
  153. buttonSendChat.circle()
  154. buttonSendChat.addTarget(self, action: #selector(sendTapped), for: .touchUpInside)
  155. buttonSendChat.backgroundColor = .mainColor
  156. if isContactCenter {
  157. buttonAckConfidential.isHidden = true
  158. constraintLeftTextField.constant = 20
  159. } else {
  160. buttonAckConfidential.circle()
  161. buttonAckConfidential.addTarget(self, action: #selector(showChooserACKConfidential), for: .touchUpInside)
  162. buttonAckConfidential.backgroundColor = .mainColor
  163. }
  164. textFieldSend.layer.cornerRadius = textFieldSend.maxCornerRadius()
  165. textFieldSend.layer.borderWidth = 1.0
  166. textFieldSend.text = "Send message".localized()
  167. textFieldSend.textColor = UIColor.lightGray
  168. textFieldSend.textContainerInset = UIEdgeInsets(top: 12, left: 20, bottom: 11, right: 40)
  169. textFieldSend.layer.borderColor = UIColor.lightGray.withAlphaComponent(0.5).cgColor
  170. textFieldSend.font = UIFont.systemFont(ofSize: 12)
  171. textFieldSend.delegate = self
  172. textFieldSend.allowsEditingTextAttributes = true
  173. navigationItem.rightBarButtonItem?.tintColor = UIColor.secondaryColor
  174. imageVideoPicker = ImageVideoPicker(presentationController: self, delegate: self)
  175. documentPicker = DocumentPicker(presentationController: self, delegate: self)
  176. let fm = FileManager.default
  177. let path = Bundle.resourcesMediaBundle(for: Nexilis.self).resourcePath!
  178. let items = try! fm.contentsOfDirectory(atPath: path)
  179. for item in items {
  180. if item.hasPrefix("sticker") {
  181. stickers.append(item)
  182. }
  183. }
  184. tableChatView.register(UITableViewCell.self, forCellReuseIdentifier: "cellEditorPersonal")
  185. loadData()
  186. setRightButtonItem()
  187. let center: NotificationCenter = NotificationCenter.default
  188. center.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
  189. center.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
  190. center.addObserver(self, selector: #selector(onReceiveMessage(notification:)), name: NSNotification.Name(rawValue: "onReceiveChat"), object: nil)
  191. center.addObserver(self, selector: #selector(onStatusChat(notification:)), name: NSNotification.Name(rawValue: "onMessageChat"), object: nil)
  192. center.addObserver(self, selector: #selector(onUploadChat(notification:)), name: NSNotification.Name(rawValue: "onUploadChat"), object: nil)
  193. center.addObserver(self, selector: #selector(onUnfriend(notification:)), name: NSNotification.Name(rawValue: "onUpdatePersonInfo"), object: nil)
  194. center.addObserver(self, selector: #selector(onTyping(notification:)), name: NSNotification.Name(rawValue: "onTypingChat"), object: nil)
  195. if dataMessageForward != nil {
  196. for i in 0..<dataMessageForward!.count {
  197. 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)
  198. }
  199. dataMessageForward = nil
  200. }
  201. if isContactCenter && !isRequestContactCenter && !onGoingCC {
  202. var companyName = ""
  203. Database.shared.database?.inTransaction({ fmdb, rollback in
  204. if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "SELECT first_name, last_name FROM BUDDY where official_account = '1'"), cursor.next() {
  205. companyName = cursor.string(forColumnIndex: 0)! + " " + cursor.string(forColumnIndex: 1)!
  206. companyName = companyName.trimmingCharacters(in: .whitespaces)
  207. cursor.close()
  208. }
  209. })
  210. self.dateStartCC = "\(Date().currentTimeMillis())"
  211. let myName = User.getData(pin: UserDefaults.standard.string(forKey: "me") as String?)
  212. 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)
  213. if channelContactCenter == "1" {
  214. if let pin = dataPerson["f_pin"] {
  215. let controller = QmeraAudioViewController()
  216. controller.user = User.getData(pin: pin)
  217. controller.isOutgoing = true
  218. controller.modalPresentationStyle = .overCurrentContext
  219. present(controller, animated: true, completion: nil)
  220. }
  221. } else if channelContactCenter == "2" {
  222. let videoVC = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "videoVCQmera") as! QmeraVideoViewController
  223. videoVC.dataPerson.append(dataPerson)
  224. self.show(videoVC, sender: nil)
  225. }
  226. }
  227. if isDirectCC {
  228. directCC()
  229. }
  230. // else if isContactCenter {
  231. // let buttonId = UIButton()
  232. // if channelContactCenter == "0" {
  233. // buttonId.tag = 0
  234. // ccAction(sender: buttonId)
  235. // } else if channelContactCenter == "1" {
  236. // buttonId.tag = 1
  237. // ccAction(sender: buttonId)
  238. // } else if channelContactCenter == "2" {
  239. // buttonId.tag = 2
  240. // ccAction(sender: buttonId)
  241. // }
  242. // }
  243. }
  244. public func afterUnfriend() {
  245. DispatchQueue.main.async {
  246. for timer in self.timerCredential.values {
  247. timer.invalidate()
  248. }
  249. self.timeoutCC.invalidate()
  250. UserDefaults.standard.removeObject(forKey: "inEditorPersonal")
  251. NotificationCenter.default.removeObserver(self)
  252. }
  253. }
  254. private func setRightButtonItem() {
  255. navigationItem.rightBarButtonItems = nil
  256. var menu = UIMenu(title: "", children: [
  257. UIAction(title: "Search".localized(), handler: {(_) in
  258. self.isSearching = true
  259. if self.reffId != nil {
  260. self.deleteReplyView()
  261. }
  262. DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
  263. let cancelButton = UIBarButtonItem(title: "Cancel".localized(), style: .plain, target: self, action: #selector(self.cancelAction))
  264. cancelButton.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.white], for: .normal)
  265. if self.dataPerson["f_pin"] != "-999" && !self.isContactCenter {
  266. self.navigationItem.rightBarButtonItems = nil
  267. }
  268. self.navigationItem.rightBarButtonItem = cancelButton
  269. if self.isContactCenter || self.fromNotification {
  270. self.navigationItem.leftBarButtonItem = nil
  271. }
  272. self.changeAppBar()
  273. self.addMultipleSelectSession()
  274. }
  275. }),
  276. UIAction(title: "Delete Conversation".localized(), handler: {(_) in
  277. if !self.isContactCenter {
  278. let alert = LibAlertController(title: "", message: "Are you sure to delete all message in this conversation?".localized(), preferredStyle: .alert)
  279. alert.addAction(UIAlertAction(title: "Cancel".localized(), style: UIAlertAction.Style.default, handler: nil))
  280. alert.addAction(UIAlertAction(title: "Delete".localized(), style: .destructive, handler: {(_) in
  281. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  282. _ = 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")
  283. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "MESSAGE_SUMMARY", _where: "l_pin='\(self.dataPerson["f_pin"]!!)'")
  284. if self.fromNotification {
  285. self.didTapExit()
  286. } else {
  287. self.navigationController?.popViewController(animated: true)
  288. }
  289. })
  290. }))
  291. self.present(alert, animated: true, completion: nil)
  292. }
  293. })
  294. ])
  295. let exblock = User.getDataCanNil(pin: self.dataPerson["f_pin"]!!)?.ex_block
  296. blocking = exblock == nil ? "0" : exblock!.isEmpty ? "0" : exblock!
  297. if blocking == "1" && self.dataPerson["f_pin"]!! != "-999" {
  298. menu = UIMenu(title: "", children: [
  299. UIAction(title: "Search".localized(), handler: {(_) in
  300. self.isSearching = true
  301. if self.reffId != nil {
  302. self.deleteReplyView()
  303. }
  304. DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
  305. let cancelButton = UIBarButtonItem(title: "Cancel".localized(), style: .plain, target: self, action: #selector(self.cancelAction))
  306. if self.dataPerson["f_pin"] != "-999" && !self.isContactCenter {
  307. self.navigationItem.rightBarButtonItems = nil
  308. }
  309. self.navigationItem.rightBarButtonItem = cancelButton
  310. if self.isContactCenter || self.fromNotification {
  311. self.navigationItem.leftBarButtonItem = nil
  312. }
  313. self.changeAppBar()
  314. self.addMultipleSelectSession()
  315. }
  316. }),
  317. UIAction(title: "Unblock".localized(), handler: {(_) in
  318. if !self.isContactCenter {
  319. DispatchQueue.global().async {
  320. if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getUnBlock(l_pin: self.dataPerson["f_pin"]!!)) {
  321. if !response.isOk() {
  322. DispatchQueue.main.async {
  323. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  324. imageView.tintColor = .white
  325. 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)
  326. banner.show()
  327. }
  328. } else {
  329. DispatchQueue.main.async { [self] in
  330. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  331. _ = Database.shared.updateRecord(fmdb: fmdb, table: "BUDDY", cvalues: [
  332. "ex_block" : "0"
  333. ], _where: "f_pin = '\(self.dataPerson["f_pin"]!!)'")
  334. })
  335. containerAction.subviews.forEach({ $0.removeFromSuperview() })
  336. containerAction.removeFromSuperview()
  337. setRightButtonItem()
  338. changeAppBar()
  339. }
  340. }
  341. } else {
  342. DispatchQueue.main.async {
  343. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  344. imageView.tintColor = .white
  345. 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)
  346. banner.show()
  347. }
  348. }
  349. }
  350. }
  351. }),
  352. UIAction(title: "Delete Conversation".localized(), handler: {(_) in
  353. if !self.isContactCenter {
  354. let alert = LibAlertController(title: "", message: "Are you sure to delete all message in this conversation?".localized(), preferredStyle: .alert)
  355. alert.addAction(UIAlertAction(title: "Cancel".localized(), style: UIAlertAction.Style.default, handler: nil))
  356. alert.addAction(UIAlertAction(title: "Delete".localized(), style: .destructive, handler: {(_) in
  357. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  358. _ = 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")
  359. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "MESSAGE_SUMMARY", _where: "l_pin='\(self.dataPerson["f_pin"]!!)'")
  360. if self.fromNotification {
  361. self.didTapExit()
  362. } else {
  363. self.navigationController?.popViewController(animated: true)
  364. }
  365. })
  366. }))
  367. self.present(alert, animated: true, completion: nil)
  368. }
  369. })
  370. ])
  371. blockedView(blocked: "1")
  372. } else if blocking == "0" {
  373. if self.dataPerson["f_pin"]!! != "-999"{
  374. menu = UIMenu(title: "", children: [
  375. UIAction(title: "Search".localized(), handler: {(_) in
  376. self.isSearching = true
  377. if self.reffId != nil {
  378. self.deleteReplyView()
  379. }
  380. DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
  381. let cancelButton = UIBarButtonItem(title: "Cancel".localized(), style: .plain, target: self, action: #selector(self.cancelAction))
  382. if self.dataPerson["f_pin"] != "-999" && !self.isContactCenter {
  383. self.navigationItem.rightBarButtonItems = nil
  384. }
  385. self.navigationItem.rightBarButtonItem = cancelButton
  386. if self.isContactCenter || self.fromNotification {
  387. self.navigationItem.leftBarButtonItem = nil
  388. }
  389. self.changeAppBar()
  390. self.addMultipleSelectSession()
  391. }
  392. }),
  393. UIAction(title: "Block".localized(), handler: {(_) in
  394. if !self.isContactCenter {
  395. DispatchQueue.global().async {
  396. if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getBlock(l_pin: self.dataPerson["f_pin"]!!)) {
  397. if !response.isOk() {
  398. DispatchQueue.main.async {
  399. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  400. imageView.tintColor = .white
  401. 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)
  402. banner.show()
  403. }
  404. } else {
  405. DispatchQueue.main.async { [self] in
  406. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  407. _ = Database.shared.updateRecord(fmdb: fmdb, table: "BUDDY", cvalues: [
  408. "ex_block" : "1"
  409. ], _where: "f_pin = '\(self.dataPerson["f_pin"]!!)'")
  410. })
  411. setRightButtonItem()
  412. changeAppBar()
  413. }
  414. }
  415. } else {
  416. DispatchQueue.main.async {
  417. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  418. imageView.tintColor = .white
  419. 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)
  420. banner.show()
  421. }
  422. }
  423. }
  424. }
  425. }),
  426. UIAction(title: "Delete Conversation".localized(), handler: {(_) in
  427. if !self.isContactCenter {
  428. let alert = LibAlertController(title: "", message: "Are you sure to delete all message in this conversation?".localized(), preferredStyle: .alert)
  429. alert.addAction(UIAlertAction(title: "Cancel".localized(), style: UIAlertAction.Style.default, handler: nil))
  430. alert.addAction(UIAlertAction(title: "Delete".localized(), style: .destructive, handler: {(_) in
  431. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  432. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "MESSAGE_SUMMARY", _where: "l_pin='\(self.dataPerson["f_pin"]!!)'")
  433. _ = 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")
  434. if self.fromNotification {
  435. self.didTapExit()
  436. } else {
  437. self.navigationController?.popViewController(animated: true)
  438. }
  439. })
  440. }))
  441. self.present(alert, animated: true, completion: nil)
  442. }
  443. })
  444. ])
  445. } else {
  446. menu = UIMenu(title: "", children: [
  447. UIAction(title: "Search".localized(), handler: {(_) in
  448. self.isSearching = true
  449. if self.reffId != nil {
  450. self.deleteReplyView()
  451. }
  452. DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
  453. let cancelButton = UIBarButtonItem(title: "Cancel".localized(), style: .plain, target: self, action: #selector(self.cancelAction))
  454. if self.dataPerson["f_pin"] != "-999" && !self.isContactCenter {
  455. self.navigationItem.rightBarButtonItems = nil
  456. }
  457. self.navigationItem.rightBarButtonItem = cancelButton
  458. if self.isContactCenter || self.fromNotification {
  459. self.navigationItem.leftBarButtonItem = nil
  460. }
  461. self.changeAppBar()
  462. self.addMultipleSelectSession()
  463. }
  464. }),
  465. UIAction(title: "Delete Conversation".localized(), handler: {(_) in
  466. if !self.isContactCenter {
  467. let alert = LibAlertController(title: "", message: "Are you sure to delete all message in this conversation?".localized(), preferredStyle: .alert)
  468. alert.addAction(UIAlertAction(title: "Cancel".localized(), style: UIAlertAction.Style.default, handler: nil))
  469. alert.addAction(UIAlertAction(title: "Delete".localized(), style: .destructive, handler: {(_) in
  470. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  471. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "MESSAGE_SUMMARY", _where: "l_pin='\(self.dataPerson["f_pin"]!!)'")
  472. _ = 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")
  473. if self.fromNotification {
  474. self.didTapExit()
  475. } else {
  476. self.navigationController?.popViewController(animated: true)
  477. }
  478. })
  479. }))
  480. self.present(alert, animated: true, completion: nil)
  481. }
  482. })
  483. ])
  484. }
  485. if containerAction.isDescendant(of: self.view) {
  486. containerAction.subviews.forEach({ $0.removeFromSuperview() })
  487. containerAction.removeFromSuperview()
  488. }
  489. } else {
  490. blockedView(blocked: "-1")
  491. changeAppBar()
  492. }
  493. let moreIcon = UIBarButtonItem(image: UIImage(systemName: "ellipsis", withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .regular, scale: .default)), menu: menu)
  494. let buttonAudioCall = UIBarButtonItem(image: UIImage(systemName: "phone", withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .regular, scale: .default)), style: .plain, target: self, action: #selector(audioVideoCall(sender:)))
  495. buttonAudioCall.tag = 0
  496. let buttonVideoCall = UIBarButtonItem(image: UIImage(systemName: "video", withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .regular, scale: .default)), style: .plain, target: self, action: #selector(audioVideoCall(sender:)))
  497. buttonVideoCall.tag = 1
  498. 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:)))
  499. if dataPerson["f_pin"] != "-999" && !isContactCenter && blocking == "0" {
  500. navigationItem.rightBarButtonItems = [moreIcon,buttonAudioCall,buttonVideoCall]
  501. } else if !isContactCenter {
  502. navigationItem.rightBarButtonItem = moreIcon
  503. } else if !complaintId.isEmpty { //!complaintId.isEmpty
  504. navigationItem.rightBarButtonItem = buttonAddRoom
  505. }
  506. }
  507. func loadData() {
  508. if (unique_l_pin != "" || isContactCenter) {
  509. getDataProfile(fPin: unique_l_pin)
  510. if isContactCenter && !isRequestContactCenter && users.count == 0 {
  511. if !unique_l_pin.isEmpty {
  512. users.append(User.getData(pin: unique_l_pin) ?? User(pin: ""))
  513. }
  514. }
  515. }
  516. if onGoingCC {
  517. UserDefaults.standard.set(self.fPinContacCenter, forKey: "inEditorPersonal")
  518. } else {
  519. UserDefaults.standard.set(dataPerson["f_pin"]!, forKey: "inEditorPersonal")
  520. }
  521. UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [dataPerson["f_pin"]!!])
  522. if isContactCenter || fromNotification {
  523. let imageButton = UIImageView(frame: CGRect(x: -16, y: 0, width: 20, height: 44))
  524. imageButton.image = UIImage(systemName: "chevron.backward", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .regular, scale: .default))?.withTintColor(.white)
  525. imageButton.contentMode = .left
  526. let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(didTapExit))
  527. imageButton.isUserInteractionEnabled = true
  528. imageButton.addGestureRecognizer(tapGestureRecognizer)
  529. let leftItem = UIBarButtonItem(customView: imageButton)
  530. self.navigationItem.leftBarButtonItem = leftItem
  531. }
  532. if dataPerson["f_pin"] == "-999" {
  533. chatbot()
  534. }
  535. let exblock = User.getData(pin: self.dataPerson["f_pin"]!!)?.ex_block
  536. blocking = exblock == nil ? "0" : exblock!.isEmpty ? "0" : exblock!
  537. changeAppBar()
  538. getData()
  539. tableChatView.alpha = 0
  540. tableChatView.delegate = self
  541. tableChatView.dataSource = self
  542. tableChatView.keyboardDismissMode = .interactive
  543. let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
  544. tapGesture.cancelsTouchesInView = false
  545. tableChatView.addGestureRecognizer(tapGesture)
  546. if !isContactCenter {
  547. getCounter()
  548. if counter > 0 && dataMessages.count >= counter {
  549. markerCounter = dataMessages[dataMessages.count - counter]["message_id"] as? String
  550. }
  551. tableChatView.alpha = 0
  552. if counter != 0 && dataMessages.count >= counter {
  553. if dataMessages.firstIndex(where: {$0["message_id"] as? String == markerCounter} ) != 0 {
  554. DispatchQueue.main.async {
  555. let data = self.dataMessages.filter({ $0["message_id"] as? String == self.markerCounter })
  556. let section = self.dataDates.firstIndex(of: data[0]["chat_date"] as! String)
  557. 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})
  558. self.tableChatView.scrollToRow(at: IndexPath(row: row!, section: section!), at: .bottom, animated: false)
  559. }
  560. } else {
  561. tableChatView.scrollToTop()
  562. }
  563. DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [self] in
  564. if currentIndexpath == nil && counter != 0 {
  565. let idMe = UserDefaults.standard.string(forKey: "me") as String?
  566. if let idx = dataMessages.firstIndex(where: { $0["message_id"] as? String == markerCounter}) {
  567. for i in idx..<dataMessages.count {
  568. if dataMessages[i]["f_pin"] as? String != idMe {
  569. sendReadMessageStatus(chat_id: "", f_pin: dataPerson["f_pin"]!!, message_scope_id: "3", message_id: dataMessages[i]["message_id"] as! String)
  570. }
  571. }
  572. counter = 0
  573. updateCounter(counter: counter)
  574. }
  575. }
  576. }
  577. } else {
  578. tableChatView.scrollToBottom(isAnimated: false)
  579. }
  580. } else if isContactCenter && onGoingCC {
  581. let idMe = UserDefaults.standard.string(forKey: "me") as String?
  582. for i in 0..<dataMessages.count {
  583. if dataMessages[i]["f_pin"] as? String != idMe {
  584. sendReadMessageStatus(chat_id: "", f_pin: dataPerson["f_pin"]!!, message_scope_id: "3", message_id: dataMessages[i]["message_id"] as! String)
  585. }
  586. }
  587. tableChatView.scrollToBottom(isAnimated: false)
  588. } else {
  589. tableChatView.scrollToBottom(isAnimated: false)
  590. }
  591. UIView.animate(withDuration: 0.5, animations: {
  592. self.tableChatView.alpha = 1.0
  593. })
  594. for data in listTimerCredential {
  595. if data.value > 0 {
  596. var second = data.value
  597. var timer = Timer()
  598. timerCredential[data.key] = timer
  599. timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: {_ in
  600. second -= 1
  601. self.listTimerCredential[data.key] = second
  602. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == data.key })
  603. if (idx != nil) {
  604. let section = self.dataDates.firstIndex(of: self.dataMessages[idx!]["chat_date"] as! String)
  605. 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 })
  606. if second == 0 {
  607. timer.invalidate()
  608. self.listTimerCredential.removeValue(forKey: data.key)
  609. self.timerCredential.removeValue(forKey: data.key)
  610. UserDefaults.standard.removeObject(forKey: data.key)
  611. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == data.key})
  612. if idx != nil {
  613. self.dataMessages[idx!]["lock"] = "2"
  614. self.dataMessages[idx!]["reff_id"] = ""
  615. }
  616. DispatchQueue.global().async {
  617. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  618. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  619. "lock" : "2"
  620. ], _where: "message_id = '\(data.key)'")
  621. })
  622. }
  623. }
  624. if row != nil && section != nil {
  625. self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
  626. }
  627. }
  628. })
  629. }
  630. }
  631. }
  632. private func chatbot() {
  633. let containerChatbot = UIView()
  634. self.view.addSubview(containerChatbot)
  635. containerChatbot.translatesAutoresizingMaskIntoConstraints = false
  636. NSLayoutConstraint.activate([
  637. containerChatbot.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
  638. containerChatbot.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
  639. containerChatbot.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
  640. containerChatbot.heightAnchor.constraint(equalToConstant: 120)
  641. ])
  642. containerChatbot.backgroundColor = .secondaryColor.withAlphaComponent(0.8)
  643. let labelChatbot = UILabel()
  644. containerChatbot.addSubview(labelChatbot)
  645. labelChatbot.translatesAutoresizingMaskIntoConstraints = false
  646. NSLayoutConstraint.activate([
  647. labelChatbot.centerYAnchor.constraint(equalTo: containerChatbot.centerYAnchor),
  648. labelChatbot.centerXAnchor.constraint(equalTo: containerChatbot.centerXAnchor),
  649. ])
  650. labelChatbot.textColor = .black
  651. labelChatbot.font = UIFont.systemFont(ofSize: 12).bold
  652. labelChatbot.text = "Interactive chatbot. Coming soon".localized()
  653. }
  654. private func changeAppBar() {
  655. let viewAppBar = UIView()
  656. viewAppBar.frame.size = CGSize(width: self.view.frame.size.width, height: 44)
  657. if !isSearching {
  658. let imageProfile = UIImageView(frame: CGRect(x: 0, y: 7, width: 30, height: 30))
  659. imageProfile.circle()
  660. imageProfile.clipsToBounds = true
  661. let pictureImage = dataPerson["picture"]!
  662. var count = 0
  663. if isContactCenter {
  664. if fPinContacCenter.isEmpty && isRequestContactCenter {
  665. getImage(name: dataPerson["picture"]!!, placeholderImage: UIImage(systemName: "person.circle.fill")!) { result, isDownloaded, image in
  666. imageProfile.image = image
  667. }
  668. viewAppBar.addSubview(imageProfile)
  669. } else {
  670. if users.count == 1 {
  671. viewAppBar.addSubview(imageProfile)
  672. getImage(name: users[0].thumb, placeholderImage: UIImage(systemName: "person.circle.fill")!) { result, isDownloaded, image in
  673. imageProfile.image = image
  674. imageProfile.contentMode = .scaleAspectFit
  675. }
  676. } else {
  677. for user in users {
  678. if count == 3 {
  679. count += 1
  680. continue
  681. }
  682. if count == 0 {
  683. let pictures = UIImageView(frame: CGRect(x: 0, y: 7, width: 30, height: 30))
  684. pictures.circle()
  685. pictures.clipsToBounds = true
  686. viewAppBar.addSubview(pictures)
  687. getImage(name: user.thumb, placeholderImage: UIImage(systemName: "person.circle.fill")!) { result, isDownloaded, image in
  688. pictures.image = image
  689. pictures.contentMode = .scaleAspectFit
  690. }
  691. } else {
  692. let pictures = UIImageView(frame: CGRect(x: count * 20 , y: 7, width: 30, height: 30))
  693. pictures.circle()
  694. pictures.clipsToBounds = true
  695. viewAppBar.addSubview(pictures)
  696. getImage(name: user.thumb, placeholderImage: UIImage(systemName: "person.circle.fill")!) { result, isDownloaded, image in
  697. pictures.image = image
  698. pictures.contentMode = .scaleAspectFit
  699. }
  700. }
  701. count += 1
  702. }
  703. }
  704. }
  705. } else if dataPerson["f_pin"]!! == "-999" {
  706. viewAppBar.addSubview(imageProfile)
  707. if Utils.getIconDock() != nil {
  708. let dataImage = try? Data(contentsOf: URL(string: Utils.getUrlDock()!)!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
  709. if dataImage != nil {
  710. imageProfile.image = UIImage(data: dataImage!)
  711. }
  712. } else {
  713. imageProfile.image = UIImage(named: "pb_button", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  714. }
  715. imageProfile.contentMode = .scaleAspectFit
  716. }
  717. else if (pictureImage != "" && pictureImage != nil) {
  718. viewAppBar.addSubview(imageProfile)
  719. imageProfile.setImage(name: pictureImage!)
  720. imageProfile.contentMode = .scaleAspectFill
  721. } else {
  722. viewAppBar.addSubview(imageProfile)
  723. imageProfile.image = UIImage(systemName: "person")
  724. imageProfile.contentMode = .scaleAspectFit
  725. imageProfile.backgroundColor = .lightGray
  726. }
  727. var titleNavigation = UILabel(frame: CGRect(x: 35, y: 0, width: viewAppBar.frame.size.width - 250, height: 44))
  728. if blocking == "-1" || blocking == "1" {
  729. titleNavigation = UILabel(frame: CGRect(x: 35, y: 0, width: viewAppBar.frame.size.width - 150, height: 44))
  730. } else if isContactCenter {
  731. titleNavigation = UILabel(frame: CGRect(x: 35, y: 0, width: viewAppBar.frame.size.width - 150, height: 44))
  732. if users.count > 0 {
  733. 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))
  734. }
  735. }
  736. viewAppBar.addSubview(titleNavigation)
  737. 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) {
  738. var name = dataPerson["name"]!!
  739. if (isContactCenter) {
  740. name = name + " " + "Contact Center".localized()
  741. titleNavigation.text = name
  742. } else {
  743. titleNavigation.set(image: UIImage(named: "ic_official_flag", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, with: " \(name)", size: 15, y: -4)
  744. }
  745. } else if User.isVerified(official_account: (dataPerson["isOfficial"] ?? "")!) && !isContactCenter {
  746. let name = dataPerson["name"]!!
  747. titleNavigation.set(image: UIImage(named: "ic_verified", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, with: " \(name)", size: 15, y: -4)
  748. } else if User.isInternal(userType: (dataPerson["user_type"] ?? "")!) && !isContactCenter {
  749. let name = dataPerson["name"]!!
  750. titleNavigation.set(image: UIImage(named: "ic_internal", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, with: " \(name)", size: 15, y: -4)
  751. } else {
  752. if !isContactCenter {
  753. titleNavigation.text = dataPerson["name"] as? String
  754. } else {
  755. if users.count == 1 {
  756. titleNavigation.text = users[0].fullName
  757. } else {
  758. var stringName = ""
  759. for user in users {
  760. if stringName.isEmpty {
  761. stringName = user.fullName
  762. } else {
  763. stringName += ", \(user.fullName)"
  764. }
  765. }
  766. titleNavigation.text = stringName
  767. }
  768. }
  769. }
  770. titleNavigation.textColor = .white
  771. titleNavigation.font = UIFont.systemFont(ofSize: 12).bold
  772. navigationItem.titleView = viewAppBar
  773. } else {
  774. searchBar = UISearchBar()
  775. searchBar.autocapitalizationType = .none
  776. searchBar.delegate = self
  777. searchBar.searchTextField.tintColor = .mainColor
  778. searchBar.barTintColor = .secondaryColor
  779. searchBar.searchTextField.backgroundColor = .secondaryColor
  780. searchBar.showsCancelButton = false
  781. searchBar.setMagnifyingGlassColorTo(color: .mainColor)
  782. navigationItem.titleView = searchBar
  783. self.definesPresentationContext = true
  784. }
  785. if copySession || forwardSession || deleteSession || isSearching {
  786. navigationItem.hidesBackButton = true
  787. navigationController?.interactivePopGestureRecognizer?.isEnabled = false
  788. } else {
  789. navigationItem.hidesBackButton = false
  790. navigationController?.interactivePopGestureRecognizer?.isEnabled = true
  791. }
  792. viewAppBar.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(seeProfileTapped)))
  793. }
  794. private func getDataProfile(fPin: String) {
  795. 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)'"
  796. if (isContactCenter && isRequestContactCenter) {
  797. query = "SELECT group_id, f_name, official, image_id FROM GROUPZ where group_type = 1 AND official = 1"
  798. }
  799. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  800. if let cursorData = Database.shared.getRecords(fmdb: fmdb, query: query) {
  801. if cursorData.next() {
  802. dataPerson["f_pin"] = cursorData.string(forColumnIndex: 0)
  803. dataPerson["name"] = cursorData.string(forColumnIndex: 1)?.trimmingCharacters(in: .whitespaces)
  804. dataPerson["picture"] = cursorData.string(forColumnIndex: 3)
  805. dataPerson["isOfficial"] = cursorData.string(forColumnIndex: 2)
  806. if isContactCenter && isRequestContactCenter {
  807. dataPerson["user_type"] = "0"
  808. } else {
  809. dataPerson["user_type"] = cursorData.string(forColumnIndex: 6)
  810. }
  811. } else {
  812. dataPerson["f_pin"] = "-999"
  813. dataPerson["name"] = "Bot"
  814. dataPerson["picture"] = ""
  815. dataPerson["isOfficial"] = ""
  816. dataPerson["deviceId"] = ""
  817. dataPerson["isOffline"] = "0"
  818. dataPerson["user_type"] = "0"
  819. }
  820. cursorData.close()
  821. }
  822. })
  823. }
  824. private func addDataMessage() {
  825. multipleOffsetUp += 1
  826. 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"
  827. 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 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"
  828. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  829. if let cursorData = Database.shared.getRecords(fmdb: fmdb, query: query) {
  830. var tempData: [[String: Any?]] = []
  831. while cursorData.next() {
  832. var row: [String: Any?] = [:]
  833. row["message_id"] = cursorData.string(forColumnIndex: 0)
  834. row["f_pin"] = cursorData.string(forColumnIndex: 1)
  835. row["l_pin"] = cursorData.string(forColumnIndex: 2)
  836. row["message_scope_id"] = cursorData.string(forColumnIndex: 3)
  837. row["server_date"] = cursorData.string(forColumnIndex: 4)
  838. row["status"] = cursorData.string(forColumnIndex: 5)
  839. row["message_text"] = cursorData.string(forColumnIndex: 6)
  840. row["audio_id"] = cursorData.string(forColumnIndex: 7)
  841. row["video_id"] = cursorData.string(forColumnIndex: 8)
  842. row["image_id"] = cursorData.string(forColumnIndex: 9)
  843. row["thumb_id"] = cursorData.string(forColumnIndex: 10)
  844. row["read_receipts"] = cursorData.string(forColumnIndex: 11)
  845. row["chat_id"] = cursorData.string(forColumnIndex: 12)
  846. row["file_id"] = cursorData.string(forColumnIndex: 13)
  847. row["attachment_flag"] = cursorData.string(forColumnIndex: 14)
  848. row["reff_id"] = cursorData.string(forColumnIndex: 15)
  849. row["lock"] = cursorData.string(forColumnIndex: 16)
  850. row["is_stared"] = cursorData.string(forColumnIndex: 17)
  851. row["blog_id"] = cursorData.string(forColumnIndex: 18)
  852. row["credential"] = cursorData.string(forColumnIndex: 19)
  853. if let cursorStatus = Database.shared.getRecords(fmdb: fmdb, query: "SELECT status FROM MESSAGE_STATUS WHERE message_id='\(row["message_id"] as! String)'") {
  854. while cursorStatus.next() {
  855. row["status"] = cursorStatus.string(forColumnIndex: 0)
  856. }
  857. cursorStatus.close()
  858. }
  859. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  860. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  861. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  862. if let dirPath = paths.first {
  863. let videoURL = URL(fileURLWithPath: dirPath).appendingPathComponent(row["video_id"] as! String)
  864. let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(row["file_id"] as! String)
  865. if ((row["video_id"] as! String) != "") {
  866. if FileManager.default.fileExists(atPath: videoURL.path){
  867. row["progress"] = 100.0
  868. } else {
  869. row["progress"] = 0.0
  870. }
  871. } else {
  872. if FileManager.default.fileExists(atPath: fileURL.path){
  873. row["progress"] = 100.0
  874. } else {
  875. row["progress"] = 0.0
  876. }
  877. }
  878. }
  879. row["chat_date"] = chatDate(stringDate: row["server_date"] as! String)
  880. row["isSelected"] = false
  881. if row["credential"] as! String == "1" {
  882. let idMe = UserDefaults.standard.string(forKey: "me")!
  883. if row["f_pin"] as! String == idMe {
  884. let second = getSecondsDifferenceFromTwoDates(start: Date.init(milliseconds: Int64(row["server_date"] as! String)!), end: Date())
  885. if second > 60 {
  886. listTimerCredential[row["message_id"] as! String] = 0
  887. row["lock"] = "2"
  888. row["reff_id"] = ""
  889. DispatchQueue.global().async {
  890. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  891. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  892. "lock" : "2"
  893. ], _where: "message_id = '\(row["message_id"] as! String)'")
  894. })
  895. }
  896. } else {
  897. let second = 60 - second
  898. listTimerCredential[row["message_id"] as! String] = second
  899. }
  900. } else {
  901. let hasMessageId = UserDefaults.standard.string(forKey: row["message_id"] as! String)
  902. if hasMessageId != nil {
  903. let second = getSecondsDifferenceFromTwoDates(start: Date.init(milliseconds: Int64(hasMessageId!)!), end: Date())
  904. if second > 60 {
  905. listTimerCredential[row["message_id"] as! String] = 0
  906. row["lock"] = "2"
  907. row["reff_id"] = ""
  908. DispatchQueue.global().async {
  909. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  910. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  911. "lock" : "2"
  912. ], _where: "message_id = '\(row["message_id"] as! String)'")
  913. })
  914. }
  915. } else {
  916. let second = 60 - second
  917. listTimerCredential[row["message_id"] as! String] = second
  918. }
  919. } else {
  920. UserDefaults.standard.set("\(Date().currentTimeMillis())", forKey: row["message_id"] as! String)
  921. listTimerCredential[row["message_id"] as! String] = 60
  922. }
  923. }
  924. }
  925. tempData.append(row)
  926. }
  927. if tempData.count != 0 && (dataMessages.firstIndex(where: { $0["message_id"] as? String == tempData[0]["message_id"] as? String }) == nil) {
  928. let lastIndex = tempData.count - 1
  929. for i in 0..<tempData.count {
  930. tableChatView.beginUpdates()
  931. dataMessages.insert(tempData[lastIndex - i], at: 0)
  932. if dataMessages.firstIndex(where: { $0["chat_date"] as? String == tempData[lastIndex - i]["chat_date"] as? String }) != nil {
  933. tableChatView.insertRows(at: [IndexPath(row: 0, section: currentIndexpath!.section)], with: .top)
  934. } else {
  935. tableChatView.insertSections(IndexSet(integer: 0), with: .top)
  936. tableChatView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .top)
  937. }
  938. tableChatView.endUpdates()
  939. }
  940. }
  941. cursorData.close()
  942. gettingDataMessage = false
  943. }
  944. })
  945. }
  946. private func getData() {
  947. // 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"
  948. // 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"
  949. 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"
  950. if isContactCenter {
  951. if complaintId.isEmpty {
  952. 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"
  953. } else {
  954. 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"
  955. }
  956. if isRequestContactCenter && !isDirectCC {
  957. var row: [String: Any?] = [:]
  958. row["f_pin"] = nil
  959. row["message_id"] = ""
  960. row["chat_date"] = "Today".localized()
  961. let listStringName: [String] = ["Chat with a Representative".localized(), "Video Call a Representative".localized(), "Call a Representative".localized()]
  962. var data : [CategoryCC] = []
  963. if Utils.getDefaultCC() == "No" {
  964. let category = CategoryCC.getDatafromParent(parent: CategoryCC.default_parent)
  965. for i in 0..<category.count {
  966. 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"))
  967. }
  968. } else {
  969. for i in 0..<listStringName.count {
  970. data.append(CategoryCC(id: "level0_\(i)", service_id: "", service_name: listStringName[i], parent: "\(i)", description: "", is_tablet: "0"))
  971. }
  972. row["attachment_flag"] = "503"
  973. }
  974. row["category_cc"] = data
  975. dataDates.append("Today".localized())
  976. dataMessages.append(row)
  977. } else if isDirectCC {
  978. dataDates.append("Today".localized())
  979. }
  980. }
  981. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  982. if let cursorData = Database.shared.getRecords(fmdb: fmdb, query: query) {
  983. while cursorData.next() {
  984. var row: [String: Any?] = [:]
  985. row["message_id"] = cursorData.string(forColumnIndex: 0)
  986. row["f_pin"] = cursorData.string(forColumnIndex: 1)
  987. row["l_pin"] = cursorData.string(forColumnIndex: 2)
  988. row["message_scope_id"] = cursorData.string(forColumnIndex: 3)
  989. row["server_date"] = cursorData.string(forColumnIndex: 4)
  990. row["status"] = cursorData.string(forColumnIndex: 5)
  991. row["message_text"] = cursorData.string(forColumnIndex: 6)
  992. row["audio_id"] = cursorData.string(forColumnIndex: 7)
  993. row["video_id"] = cursorData.string(forColumnIndex: 8)
  994. row["image_id"] = cursorData.string(forColumnIndex: 9)
  995. row["thumb_id"] = cursorData.string(forColumnIndex: 10)
  996. row["read_receipts"] = cursorData.string(forColumnIndex: 11)
  997. row["chat_id"] = cursorData.string(forColumnIndex: 12)
  998. row["file_id"] = cursorData.string(forColumnIndex: 13)
  999. row["attachment_flag"] = cursorData.string(forColumnIndex: 14)
  1000. row["reff_id"] = cursorData.string(forColumnIndex: 15)
  1001. row["lock"] = cursorData.string(forColumnIndex: 16)
  1002. row["is_stared"] = cursorData.string(forColumnIndex: 17)
  1003. row["blog_id"] = cursorData.string(forColumnIndex: 18)
  1004. row["credential"] = cursorData.string(forColumnIndex: 19)
  1005. if let cursorStatus = Database.shared.getRecords(fmdb: fmdb, query: "SELECT status FROM MESSAGE_STATUS WHERE message_id='\(row["message_id"] as! String)'") {
  1006. while cursorStatus.next() {
  1007. row["status"] = cursorStatus.string(forColumnIndex: 0)
  1008. }
  1009. cursorStatus.close()
  1010. }
  1011. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  1012. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  1013. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  1014. if let dirPath = paths.first {
  1015. let videoURL = URL(fileURLWithPath: dirPath).appendingPathComponent(row["video_id"] as! String)
  1016. let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(row["file_id"] as! String)
  1017. if ((row["video_id"] as! String) != "") {
  1018. if FileManager.default.fileExists(atPath: videoURL.path){
  1019. row["progress"] = 100.0
  1020. } else {
  1021. row["progress"] = 0.0
  1022. }
  1023. } else {
  1024. if FileManager.default.fileExists(atPath: fileURL.path){
  1025. row["progress"] = 100.0
  1026. } else {
  1027. row["progress"] = 0.0
  1028. }
  1029. }
  1030. }
  1031. row["chat_date"] = chatDate(stringDate: row["server_date"] as! String)
  1032. row["isSelected"] = false
  1033. if row["credential"] as! String == "1" {
  1034. let idMe = UserDefaults.standard.string(forKey: "me")!
  1035. if row["f_pin"] as! String == idMe {
  1036. let second = getSecondsDifferenceFromTwoDates(start: Date.init(milliseconds: Int64(row["server_date"] as! String)!), end: Date())
  1037. if second > 60 {
  1038. listTimerCredential[row["message_id"] as! String] = 0
  1039. row["lock"] = "2"
  1040. row["reff_id"] = ""
  1041. DispatchQueue.global().async {
  1042. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  1043. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  1044. "lock" : "2"
  1045. ], _where: "message_id = '\(row["message_id"] as! String)'")
  1046. })
  1047. }
  1048. } else {
  1049. let second = 60 - second
  1050. listTimerCredential[row["message_id"] as! String] = second
  1051. }
  1052. } else {
  1053. let hasMessageId = UserDefaults.standard.string(forKey: row["message_id"] as! String)
  1054. if hasMessageId != nil {
  1055. let second = getSecondsDifferenceFromTwoDates(start: Date.init(milliseconds: Int64(hasMessageId!)!), end: Date())
  1056. if second > 60 {
  1057. listTimerCredential[row["message_id"] as! String] = 0
  1058. row["lock"] = "2"
  1059. row["reff_id"] = ""
  1060. DispatchQueue.global().async {
  1061. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  1062. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  1063. "lock" : "2"
  1064. ], _where: "message_id = '\(row["message_id"] as! String)'")
  1065. })
  1066. }
  1067. } else {
  1068. let second = 60 - second
  1069. listTimerCredential[row["message_id"] as! String] = second
  1070. }
  1071. } else {
  1072. UserDefaults.standard.set("\(Date().currentTimeMillis())", forKey: row["message_id"] as! String)
  1073. listTimerCredential[row["message_id"] as! String] = 60
  1074. }
  1075. }
  1076. }
  1077. dataMessages.append(row)
  1078. }
  1079. cursorData.close()
  1080. }
  1081. })
  1082. }
  1083. func getSecondsDifferenceFromTwoDates(start: Date, end: Date) -> Int {
  1084. let diff = Int(end.timeIntervalSince1970 - start.timeIntervalSince1970)
  1085. let hours = diff / 3600
  1086. let seconds = (diff - hours * 3600)
  1087. return seconds
  1088. }
  1089. func chatDate(stringDate: String) -> String {
  1090. let date = Date(milliseconds: Int64(stringDate)!)
  1091. let calendar = Calendar.current
  1092. if (calendar.isDateInToday(date)) {
  1093. if !dataDates.contains("Today".localized()){
  1094. dataDates.append("Today".localized())
  1095. }
  1096. return "Today".localized()
  1097. } else {
  1098. let startOfNow = calendar.startOfDay(for: Date())
  1099. let startOfTimeStamp = calendar.startOfDay(for: date)
  1100. let components = calendar.dateComponents([.day], from: startOfNow, to: startOfTimeStamp)
  1101. let day = -(components.day!)
  1102. if day == 1{
  1103. if !dataDates.contains("Yesterday".localized()){
  1104. dataDates.append("Yesterday".localized())
  1105. }
  1106. return "Yesterday".localized()
  1107. } else if day < 7 {
  1108. if !dataDates.contains("\(day) " + "days".localized() + " " + "ago".localized()){
  1109. dataDates.append("\(day) " + "days".localized() + " " + "ago".localized())
  1110. }
  1111. return "\(day) " + "days".localized() + " " + "ago".localized()
  1112. } else {
  1113. let formatter = DateFormatter()
  1114. formatter.dateFormat = "dd MMMM yyyy"
  1115. formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
  1116. let stringFormat = formatter.string(from: date as Date)
  1117. if !dataDates.contains(stringFormat){
  1118. dataDates.append(stringFormat)
  1119. }
  1120. return stringFormat
  1121. }
  1122. }
  1123. }
  1124. @objc func onUploadChat(notification: NSNotification) {
  1125. let data:[AnyHashable : Any] = notification.userInfo!
  1126. var isImage = false
  1127. var idx = dataMessages.lastIndex(where: { $0["video_id"] as? String == data["name"] as? String })
  1128. if (idx == nil) {
  1129. idx = dataMessages.lastIndex(where: { $0["image_id"] as? String == data["name"] as? String })
  1130. isImage = true
  1131. }
  1132. if (idx != nil) {
  1133. let section = dataDates.firstIndex(of: dataMessages[idx!]["chat_date"] as! String)
  1134. if section == nil {
  1135. return
  1136. }
  1137. let row = dataMessages.filter({ $0["chat_date"] as! String == dataDates[section!]}).firstIndex(where: { $0["message_id"] as? String == dataMessages[idx!]["message_id"] as? String})
  1138. if row == nil {
  1139. return
  1140. }
  1141. DispatchQueue.main.async {
  1142. let indexPath = IndexPath(row: row!, section: section!)
  1143. if let cell = self.tableChatView.cellForRow(at: indexPath) {
  1144. for view in cell.contentView.subviews {
  1145. if !(view is UILabel) && !(view is UIImageView) {
  1146. for viewInContainer in view.subviews {
  1147. if viewInContainer is UIImageView {
  1148. if viewInContainer.subviews.count == 0 {
  1149. return
  1150. }
  1151. var containerView = UIView()
  1152. if (isImage) {
  1153. containerView = viewInContainer.subviews[0]
  1154. } else {
  1155. containerView = viewInContainer.subviews[1]
  1156. }
  1157. let loading = containerView.layer.sublayers![1] as! CAShapeLayer
  1158. loading.strokeEnd = CGFloat(data["progress"] as! Double / 100)
  1159. if (data["progress"] as! Double == 100.0) {
  1160. self.dataMessages[idx!]["progress"] = data["progress"]
  1161. self.tableChatView.reloadRows(at: [indexPath], with: .none)
  1162. }
  1163. }
  1164. }
  1165. }
  1166. }
  1167. }
  1168. }
  1169. } else {
  1170. idx = dataMessages.lastIndex(where: { $0["file_id"] as? String == data["name"] as? String })
  1171. if (idx != nil) {
  1172. let section = dataDates.firstIndex(of: dataMessages[idx!]["chat_date"] as! String)
  1173. if section == nil {
  1174. return
  1175. }
  1176. let row = dataMessages.filter({ $0["chat_date"] as! String == dataDates[section!]}).firstIndex(where: { $0["message_id"] as? String == dataMessages[idx!]["message_id"] as? String})
  1177. if row == nil {
  1178. return
  1179. }
  1180. DispatchQueue.main.async {
  1181. let indexPath = IndexPath(row: row!, section: section!)
  1182. if let cell = self.tableChatView.cellForRow(at: indexPath) {
  1183. for view in cell.contentView.subviews {
  1184. if !(view is UILabel) && !(view is UIImageView) {
  1185. for viewSubviews in view.subviews {
  1186. if !(viewSubviews is UILabel) {
  1187. for viewInContainer in viewSubviews.subviews {
  1188. if !(viewInContainer is UILabel) && !(viewInContainer is UIImageView) {
  1189. if viewInContainer.layer.sublayers!.count < 2 {
  1190. return
  1191. }
  1192. let loading = viewInContainer.layer.sublayers![1] as! CAShapeLayer
  1193. loading.strokeEnd = CGFloat(data["progress"] as! Double / 100)
  1194. if (data["progress"] as! Double == 100.0) {
  1195. self.dataMessages[idx!]["progress"] = data["progress"]
  1196. self.tableChatView.reloadRows(at: [indexPath], with: .none)
  1197. }
  1198. }
  1199. }
  1200. }
  1201. }
  1202. }
  1203. }
  1204. }
  1205. }
  1206. }
  1207. }
  1208. }
  1209. @objc func onReceiveMessage(notification: NSNotification) {
  1210. DispatchQueue.main.async { [self] in
  1211. let data:[AnyHashable : Any] = notification.userInfo!
  1212. if let dataMessage = data["message"] as? TMessage {
  1213. let chatData = dataMessage.mBodies
  1214. if (dataMessage.getCode() == CoreMessage_TMessageCode.PUSH_MEMBER_ROOM_CONTACT_CENTER && isContactCenter) {
  1215. let data = dataMessage.getBody(key: CoreMessage_TMessageKey.DATA)
  1216. if !data.isEmpty {
  1217. if let jsonArray = try! JSONSerialization.jsonObject(with: data.data(using: String.Encoding.utf8)!, options: JSONSerialization.ReadingOptions()) as? [AnyObject] {
  1218. var members = ""
  1219. let idMe = UserDefaults.standard.string(forKey: "me")!
  1220. var user : [User] = []
  1221. for json in jsonArray {
  1222. if "\(json)" != idMe {
  1223. if members.isEmpty {
  1224. members = "\(json)"
  1225. } else {
  1226. members += ",\(json)"
  1227. }
  1228. if let userData = User.getData(pin: "\(json)") {
  1229. user.append(userData)
  1230. } else {
  1231. Nexilis.addFriend (fpin: "\(json)") { result in
  1232. DispatchQueue.main.async {
  1233. if result {
  1234. let userData = User.getData(pin: "\(json)")!
  1235. user.append(userData)
  1236. } else {
  1237. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  1238. imageView.tintColor = .white
  1239. 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)
  1240. banner.show()
  1241. }
  1242. }
  1243. }
  1244. }
  1245. }
  1246. }
  1247. self.users = user
  1248. self.fPinContacCenter = members
  1249. self.changeAppBar()
  1250. UserDefaults.standard.set(members, forKey: "inEditorPersonal")
  1251. }
  1252. }
  1253. } else if (dataMessage.getCode() == CoreMessage_TMessageCode.ACCEPT_CALL_CENTER) {
  1254. if !self.isRequestContactCenter || !isContactCenter {
  1255. return
  1256. }
  1257. UserDefaults.standard.set(dataMessage.getBody(key: CoreMessage_TMessageKey.F_PIN), forKey: "inEditorPersonal")
  1258. let date = Date()
  1259. let formatter = DateFormatter()
  1260. formatter.dateFormat = "HH:mm"
  1261. formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
  1262. self.fPinContacCenter = dataMessage.getBody(key: CoreMessage_TMessageKey.F_PIN)
  1263. self.complaintId = dataMessage.getBody(key: CoreMessage_TMessageKey.DATA)
  1264. self.channelContactCenter = dataMessage.getBody(key: CoreMessage_TMessageKey.CHANNEL)
  1265. var row: [String: Any?] = [:]
  1266. 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()
  1267. row["message_id"] = ""
  1268. 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()
  1269. row["chat_date"] = "Today".localized()
  1270. self.dataMessages.append(row)
  1271. self.users.append(User.getData(pin: dataMessage.getBody(key: CoreMessage_TMessageKey.F_PIN))!)
  1272. self.changeAppBar()
  1273. self.setRightButtonItem()
  1274. self.dateStartCC = "\(Date().currentTimeMillis())"
  1275. self.tableChatView.insertRows(at: [IndexPath(row: self.dataMessages.count - 1, section: 0)], with: .none)
  1276. self.tableChatView.scrollToBottom()
  1277. UserDefaults.standard.removeObject(forKey: "waitingRequestCC")
  1278. if dataMessage.getBody(key: CoreMessage_TMessageKey.CHANNEL) != "0" {
  1279. UserDefaults.standard.set("\(Date().currentTimeMillis())", forKey: "startTimeCC")
  1280. UserDefaults.standard.set(dataMessage.getBody(key: CoreMessage_TMessageKey.CHANNEL), forKey: "channelCC")
  1281. DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
  1282. self.dismiss(animated: true, completion: nil)
  1283. })
  1284. }
  1285. } 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 {
  1286. let onGoingCC = UserDefaults.standard.string(forKey: "onGoingCC") ?? ""
  1287. if onGoingCC.isEmpty || !isContactCenter {
  1288. return
  1289. }
  1290. let requester = onGoingCC.components(separatedBy: ",")[0]
  1291. let officer = onGoingCC.isEmpty ? "" : onGoingCC.components(separatedBy: ",")[1]
  1292. let fPin = dataMessage.getCode() == CoreMessage_TMessageCode.END_CALL_CENTER ? chatData[CoreMessage_TMessageKey.F_PIN] : dataMessage.getPIN()
  1293. if fPin == officer || fPin == requester {
  1294. DispatchQueue.global().async {
  1295. let date = "\(Date().currentTimeMillis())"
  1296. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  1297. do {
  1298. _ = try Database.shared.insertRecord(fmdb: fmdb, table: "CALL_CENTER_HISTORY", cvalues: [
  1299. "type" : self.channelContactCenter,
  1300. "title" : "Contact Center".localized(),
  1301. "time" : self.dateStartCC,
  1302. "f_pin" : officer,
  1303. "data" : self.complaintId,
  1304. "time_end" : date,
  1305. "complaint_id" : self.complaintId,
  1306. "members" : "",
  1307. "requester": requester
  1308. ], replace: true)
  1309. } catch {
  1310. rollback.pointee = true
  1311. //print(error)
  1312. }
  1313. })
  1314. }
  1315. self.dismissKeyboard()
  1316. self.disableEditor()
  1317. UserDefaults.standard.removeObject(forKey: "onGoingCC")
  1318. UserDefaults.standard.removeObject(forKey: "membersCC")
  1319. UserDefaults.standard.removeObject(forKey: "waitingRequestCC")
  1320. DispatchQueue.main.async {
  1321. let imageView = UIImageView(image: UIImage(systemName: "info.circle"))
  1322. imageView.tintColor = .white
  1323. 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)
  1324. banner.show()
  1325. }
  1326. timeoutCC.invalidate()
  1327. DispatchQueue.main.asyncAfter(deadline: .now() + 1.5, execute: {
  1328. if !(self.presentedViewController is EditorPersonal) {
  1329. self.dismiss(animated: true, completion: nil)
  1330. }
  1331. DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
  1332. self.dismiss(animated: true, completion: nil)
  1333. }
  1334. })
  1335. } else {
  1336. var members = ""
  1337. self.users.removeAll(where: {$0.pin == fPin})
  1338. for user in self.users {
  1339. if members.isEmpty {
  1340. members = "\(user.pin)"
  1341. } else {
  1342. members = ",\(user.pin)"
  1343. }
  1344. }
  1345. UserDefaults.standard.set("\(members)", forKey: "membersCC")
  1346. self.fPinContacCenter = members
  1347. self.changeAppBar()
  1348. }
  1349. }
  1350. 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]) {
  1351. if chatData[CoreMessage_TMessageKey.F_PIN] == nil {
  1352. return
  1353. }
  1354. var row: [String: Any?] = [:]
  1355. row["message_id"] = chatData[CoreMessage_TMessageKey.MESSAGE_ID]
  1356. row["f_pin"] = chatData[CoreMessage_TMessageKey.F_PIN]
  1357. row["l_pin"] = chatData[CoreMessage_TMessageKey.L_PIN]
  1358. row["message_scope_id"] = chatData[CoreMessage_TMessageKey.MESSAGE_SCOPE_ID]
  1359. row["server_date"] = chatData[CoreMessage_TMessageKey.SERVER_DATE]
  1360. row["status"] = chatData[CoreMessage_TMessageKey.STATUS]
  1361. row["message_text"] = chatData[CoreMessage_TMessageKey.MESSAGE_TEXT]
  1362. if (chatData.keys.contains(CoreMessage_TMessageKey.AUDIO_ID)) {
  1363. row["audio_id"] = chatData[CoreMessage_TMessageKey.AUDIO_ID]
  1364. } else {
  1365. row["audio_id"] = ""
  1366. }
  1367. if (chatData.keys.contains(CoreMessage_TMessageKey.VIDEO_ID)) {
  1368. row["video_id"] = chatData[CoreMessage_TMessageKey.VIDEO_ID]
  1369. } else {
  1370. row["video_id"] = ""
  1371. }
  1372. if (chatData.keys.contains(CoreMessage_TMessageKey.IMAGE_ID)) {
  1373. row["image_id"] = chatData[CoreMessage_TMessageKey.IMAGE_ID]
  1374. } else {
  1375. row["image_id"] = ""
  1376. }
  1377. if (chatData.keys.contains(CoreMessage_TMessageKey.THUMB_ID)) {
  1378. row["thumb_id"] = chatData[CoreMessage_TMessageKey.THUMB_ID]
  1379. } else {
  1380. row["thumb_id"] = ""
  1381. }
  1382. if (chatData.keys.contains(CoreMessage_TMessageKey.READ_RECEIPTS)) {
  1383. row["read_receipts"] = chatData[CoreMessage_TMessageKey.READ_RECEIPTS]
  1384. } else {
  1385. row["read_receipts"] = ""
  1386. }
  1387. if (chatData.keys.contains(CoreMessage_TMessageKey.CREDENTIAL)) {
  1388. row["credential"] = chatData[CoreMessage_TMessageKey.CREDENTIAL]
  1389. } else {
  1390. row["credential"] = ""
  1391. }
  1392. row["chat_id"] = ""
  1393. if (chatData.keys.contains(CoreMessage_TMessageKey.FILE_ID)) {
  1394. row["file_id"] = chatData[CoreMessage_TMessageKey.FILE_ID]
  1395. } else {
  1396. row["file_id"] = ""
  1397. }
  1398. row["progress"] = 0.0
  1399. row["attachment_flag"] = chatData[CoreMessage_TMessageKey.ATTACHMENT_FLAG]
  1400. row["reff_id"] = chatData[CoreMessage_TMessageKey.REF_ID] ?? ""
  1401. row["lock"] = ""
  1402. row["is_stared"] = "0"
  1403. row["isSelected"] = false
  1404. if !self.dataDates.contains("Today".localized()) {
  1405. self.dataDates.append("Today".localized())
  1406. self.tableChatView.insertSections(IndexSet(integer: self.dataDates.count - 1), with: .none)
  1407. }
  1408. row["chat_date"] = "Today".localized()
  1409. row["blog_id"] = chatData[CoreMessage_TMessageKey.BLOG_ID]
  1410. self.counter += 1
  1411. if row["credential"] as! String == "1" {
  1412. self.listTimerCredential[row["message_id"] as! String] = 60
  1413. }
  1414. self.dataMessages.append(row)
  1415. 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)
  1416. if row["credential"] as! String == "1" {
  1417. var timer = Timer()
  1418. var minute = 60
  1419. self.timerCredential[row["message_id"] as! String] = timer
  1420. UserDefaults.standard.set("\(Date().currentTimeMillis())", forKey: row["message_id"] as! String)
  1421. timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: {_ in
  1422. minute -= 1
  1423. self.listTimerCredential[row["message_id"] as! String] = minute
  1424. if minute == 0 {
  1425. timer.invalidate()
  1426. self.listTimerCredential.removeValue(forKey: row["message_id"] as! String)
  1427. self.timerCredential.removeValue(forKey: row["message_id"] as! String)
  1428. UserDefaults.standard.removeObject(forKey: row["message_id"] as! String)
  1429. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == row["message_id"] as? String})
  1430. if idx != nil {
  1431. self.dataMessages[idx!]["lock"] = "2"
  1432. self.dataMessages[idx!]["reff_id"] = ""
  1433. }
  1434. DispatchQueue.global().async {
  1435. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  1436. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  1437. "lock" : "2"
  1438. ], _where: "message_id = '\(row["message_id"] as! String)'")
  1439. })
  1440. }
  1441. }
  1442. self.tableChatView.reloadRows(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)
  1443. })
  1444. }
  1445. if self.isContactCenter {
  1446. let idMe = UserDefaults.standard.string(forKey: "me")!
  1447. let onGoingCC = UserDefaults.standard.string(forKey: "onGoingCC") ?? ""
  1448. let officer = onGoingCC.isEmpty ? "" : onGoingCC.components(separatedBy: ",")[1]
  1449. if officer == idMe {
  1450. self.timeoutCC.invalidate()
  1451. } else {
  1452. if !self.showToast30s {
  1453. self.showToast(message: "Please reply within 30 seconds so the call center session doesn't end.".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
  1454. sendTyping(l_pin: fPinContacCenter, isTyping: true)
  1455. self.showToast30s = true
  1456. }
  1457. }
  1458. }
  1459. if chatData[CoreMessage_TMessageKey.FORMAT] == "1" {
  1460. 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]!)
  1461. self.tableChatView.scrollToBottom()
  1462. } else if self.currentIndexpath?.row == (self.dataMessages.count - 2) {
  1463. if (self.viewIfLoaded?.window != nil) {
  1464. 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]!)
  1465. }
  1466. self.tableChatView.scrollToBottom()
  1467. 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) {
  1468. self.counter = 0
  1469. self.updateCounter(counter: self.counter)
  1470. }
  1471. let lastMarkerCounter = markerCounter
  1472. if self.markerCounter != nil {
  1473. self.markerCounter = nil
  1474. }
  1475. self.tableChatView.beginUpdates()
  1476. let indexMessage = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == lastMarkerCounter })
  1477. if indexMessage != nil {
  1478. let section = self.dataDates.firstIndex(of: self.dataMessages[indexMessage!]["chat_date"] as! String)
  1479. 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 })
  1480. if row != nil && section != nil {
  1481. self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
  1482. }
  1483. }
  1484. self.tableChatView.endUpdates()
  1485. }
  1486. else if self.currentIndexpath == nil {
  1487. self.counter = 0
  1488. self.updateCounter(counter: self.counter)
  1489. if (self.viewIfLoaded?.window != nil) {
  1490. 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]!)
  1491. }
  1492. }
  1493. else if self.counter != 0 {
  1494. if !self.indicatorCounterBSTB.isDescendant(of: self.view) && self.buttonScrollToBottom.isDescendant(of: self.view) {
  1495. self.markerCounter = row["message_id"] as? String
  1496. self.addCounterAtButttonScrollToBottom()
  1497. self.tableChatView.beginUpdates()
  1498. let indexMessage = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == self.markerCounter })
  1499. if indexMessage != nil {
  1500. let section = self.dataDates.firstIndex(of: self.dataMessages[indexMessage!]["chat_date"] as! String)
  1501. 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 })
  1502. if row != nil && section != nil {
  1503. self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
  1504. }
  1505. }
  1506. self.tableChatView.endUpdates()
  1507. } else if self.indicatorCounterBSTB.isDescendant(of: self.view) {
  1508. self.labelCounter.text = "\(self.counter)"
  1509. }
  1510. }
  1511. }
  1512. } else if !self.isContactCenter {
  1513. NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
  1514. }
  1515. }
  1516. }
  1517. private func disableEditor() {
  1518. view.addSubview(containerAction)
  1519. containerAction.translatesAutoresizingMaskIntoConstraints = false
  1520. NSLayoutConstraint.activate([
  1521. containerAction.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
  1522. containerAction.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
  1523. containerAction.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
  1524. containerAction.heightAnchor.constraint(equalToConstant: 120)
  1525. ])
  1526. containerAction.backgroundColor = .secondaryColor.withAlphaComponent(0.8)
  1527. let labelDisable = UILabel()
  1528. containerAction.addSubview(labelDisable)
  1529. labelDisable.translatesAutoresizingMaskIntoConstraints = false
  1530. NSLayoutConstraint.activate([
  1531. labelDisable.centerYAnchor.constraint(equalTo: containerAction.centerYAnchor),
  1532. labelDisable.centerXAnchor.constraint(equalTo: containerAction.centerXAnchor),
  1533. ])
  1534. labelDisable.textColor = .black
  1535. labelDisable.font = UIFont.systemFont(ofSize: 12).bold
  1536. labelDisable.text = "Call center session is over".localized()
  1537. }
  1538. @objc func onStatusChat(notification: NSNotification) {
  1539. DispatchQueue.main.async {
  1540. let data:[AnyHashable : Any] = notification.userInfo!
  1541. if let dataMessage = data["message"] as? TMessage {
  1542. let chatData = dataMessage.mBodies
  1543. let onGoingCC = UserDefaults.standard.string(forKey: "onGoingCC") ?? ""
  1544. let requester = onGoingCC.components(separatedBy: ",")[0]
  1545. let idMe = UserDefaults.standard.string(forKey: "me")!
  1546. 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 {
  1547. if (chatData.keys.contains(CoreMessage_TMessageKey.MESSAGE_ID) && !(chatData[CoreMessage_TMessageKey.MESSAGE_ID]!).contains("-2,")) {
  1548. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == chatData[CoreMessage_TMessageKey.MESSAGE_ID]! })
  1549. if (idx != nil) {
  1550. if (chatData[CoreMessage_TMessageKey.DELETE_MESSAGE_FLAG] == "1") {
  1551. self.dataMessages[idx!]["lock"] = "1"
  1552. self.dataMessages[idx!]["reff_id"] = ""
  1553. let section = self.dataDates.firstIndex(of: self.dataMessages[idx!]["chat_date"] as! String)
  1554. 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 })
  1555. if row != nil && section != nil {
  1556. self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
  1557. }
  1558. if self.listTimerCredential[self.dataMessages[idx!]["message_id"] as! String] != nil {
  1559. self.listTimerCredential.removeValue(forKey: self.dataMessages[idx!]["message_id"] as! String)
  1560. self.timerCredential[self.dataMessages[idx!]["message_id"] as! String]?.invalidate()
  1561. self.timerCredential.removeValue(forKey: self.dataMessages[idx!]["message_id"] as! String)
  1562. UserDefaults.standard.removeObject(forKey: self.dataMessages[idx!]["message_id"] as! String)
  1563. }
  1564. if self.reffId != nil && self.reffId == chatData[CoreMessage_TMessageKey.MESSAGE_ID]! {
  1565. self.deleteReplyView()
  1566. }
  1567. } else {
  1568. self.dataMessages[idx!]["status"] = chatData[CoreMessage_TMessageKey.STATUS]!
  1569. let section = self.dataDates.firstIndex(of: self.dataMessages[idx!]["chat_date"] as! String)
  1570. 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 })
  1571. if row != nil && section != nil {
  1572. self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
  1573. }
  1574. }
  1575. }
  1576. }
  1577. else if (chatData.keys.contains("message_id")) {
  1578. let idx = self.dataMessages.firstIndex(where: { "'\(String(describing: $0["message_id"] as? String))'" == chatData["message_id"]! })
  1579. if (idx != nil) {
  1580. if (chatData[CoreMessage_TMessageKey.DELETE_MESSAGE_FLAG] == "1") {
  1581. self.dataMessages[idx!]["lock"] = "1"
  1582. self.dataMessages[idx!]["reff_id"] = ""
  1583. let section = self.dataDates.firstIndex(of: self.dataMessages[idx!]["chat_date"] as! String)
  1584. 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 })
  1585. if row != nil && section != nil {
  1586. self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
  1587. }
  1588. if self.listTimerCredential[self.dataMessages[idx!]["message_id"] as! String] != nil {
  1589. self.listTimerCredential.removeValue(forKey: self.dataMessages[idx!]["message_id"] as! String)
  1590. self.timerCredential[self.dataMessages[idx!]["message_id"] as! String]?.invalidate()
  1591. self.timerCredential.removeValue(forKey: self.dataMessages[idx!]["message_id"] as! String)
  1592. UserDefaults.standard.removeObject(forKey: self.dataMessages[idx!]["message_id"] as! String)
  1593. }
  1594. if self.reffId != nil && self.reffId == chatData["message_id"]! {
  1595. self.deleteReplyView()
  1596. }
  1597. } else {
  1598. self.dataMessages[idx!]["status"] = chatData[CoreMessage_TMessageKey.STATUS]!
  1599. let section = self.dataDates.firstIndex(of: self.dataMessages[idx!]["chat_date"] as! String)
  1600. 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 })
  1601. if row != nil && section != nil {
  1602. self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
  1603. }
  1604. }
  1605. }
  1606. }
  1607. else {
  1608. let messageId = chatData[CoreMessage_TMessageKey.MESSAGE_ID]!.split(separator: ",")[1]
  1609. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String ?? "" == messageId })
  1610. if (idx != nil) {
  1611. self.dataMessages[idx!]["status"] = chatData[CoreMessage_TMessageKey.STATUS]!
  1612. let section = self.dataDates.firstIndex(of: self.dataMessages[idx!]["chat_date"] as! String)
  1613. 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 })
  1614. if row != nil && section != nil {
  1615. self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
  1616. }
  1617. }
  1618. }
  1619. }
  1620. }
  1621. }
  1622. }
  1623. @objc func onTyping(notification: NSNotification) {
  1624. DispatchQueue.main.async { [self] in
  1625. let data:[AnyHashable : Any] = notification.userInfo!
  1626. let message: TMessage = data["message"] as! TMessage
  1627. let onGoingCC = UserDefaults.standard.string(forKey: "onGoingCC") ?? ""
  1628. if !onGoingCC.isEmpty {
  1629. let officer = onGoingCC.isEmpty ? "" : onGoingCC.components(separatedBy: ",")[1]
  1630. if message.getBody(key: CoreMessage_TMessageKey.F_PIN) != officer {
  1631. //print("RESET TIMER")
  1632. // timeoutCC.invalidate()
  1633. // timeoutCC = Timer.scheduledTimer(withTimeInterval: 30.0, repeats: false, block: {_ in
  1634. // let imageView = UIImageView(image: UIImage(systemName: "info.circle"))
  1635. // imageView.tintColor = .white
  1636. // 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)
  1637. // banner.show()
  1638. // self.endCallCenter()
  1639. // })
  1640. }
  1641. } else {
  1642. }
  1643. }
  1644. }
  1645. @objc func onUnfriend(notification: NSNotification) {
  1646. let data:[AnyHashable : Any] = notification.userInfo!
  1647. DispatchQueue.main.async { [self] in
  1648. if data["state"] as! Int == 99 && (data["message"] as! String).components(separatedBy: ",")[0] == "delete_buddy" {
  1649. removed = true
  1650. if forwardSession || copySession || deleteSession || isSearching {
  1651. cancelAction()
  1652. }
  1653. DispatchQueue.main.asyncAfter(deadline: .now() + 0.35, execute: {[self] in
  1654. navigationItem.rightBarButtonItem = nil
  1655. navigationItem.rightBarButtonItems = nil
  1656. changeAppBar()
  1657. view.addSubview(containerAction)
  1658. containerAction.translatesAutoresizingMaskIntoConstraints = false
  1659. NSLayoutConstraint.activate([
  1660. containerAction.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
  1661. containerAction.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
  1662. containerAction.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
  1663. containerAction.heightAnchor.constraint(equalToConstant: 120)
  1664. ])
  1665. containerAction.backgroundColor = .secondaryColor.withAlphaComponent(0.8)
  1666. let labelUnfriend = UILabel()
  1667. containerAction.addSubview(labelUnfriend)
  1668. labelUnfriend.translatesAutoresizingMaskIntoConstraints = false
  1669. NSLayoutConstraint.activate([
  1670. labelUnfriend.centerYAnchor.constraint(equalTo: containerAction.centerYAnchor),
  1671. labelUnfriend.centerXAnchor.constraint(equalTo: containerAction.centerXAnchor),
  1672. ])
  1673. labelUnfriend.textColor = .black
  1674. labelUnfriend.font = UIFont.systemFont(ofSize: 12).bold
  1675. labelUnfriend.text = "You have unfriended this user".localized()
  1676. NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
  1677. if contactChatNav.viewIfLoaded?.window != nil {
  1678. contactChatNav.dismiss(animated: true)
  1679. }
  1680. DispatchQueue.main.asyncAfter(deadline: .now() + 0.35, execute: {
  1681. if self.fromNotification {
  1682. self.didTapExit()
  1683. } else {
  1684. self.navigationController?.popViewController(animated: true)
  1685. }
  1686. })
  1687. })
  1688. } else if data["state"] as! Int == 01 {
  1689. if let dataMessage = try! JSONSerialization.jsonObject(with: (data["message"] as! String).data(using: .utf8)!, options: []) as? [String: String] {
  1690. if(dataMessage["l_pin"] == dataPerson["f_pin"]!){
  1691. if let block = dataMessage["block"] {
  1692. if(block == "-1"){
  1693. dismissKeyboard()
  1694. }
  1695. blockedView(blocked: block)
  1696. if contactChatNav.viewIfLoaded?.window != nil {
  1697. contactChatNav.dismiss(animated: true)
  1698. }
  1699. cancelAction()
  1700. }
  1701. }
  1702. setRightButtonItem()
  1703. }
  1704. }
  1705. }
  1706. }
  1707. func blockedView(blocked: String) {
  1708. dismissKeyboard()
  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 labelBlocked = UILabel()
  1719. containerAction.addSubview(labelBlocked)
  1720. labelBlocked.translatesAutoresizingMaskIntoConstraints = false
  1721. NSLayoutConstraint.activate([
  1722. labelBlocked.centerYAnchor.constraint(equalTo: containerAction.centerYAnchor),
  1723. labelBlocked.centerXAnchor.constraint(equalTo: containerAction.centerXAnchor),
  1724. ])
  1725. labelBlocked.textColor = .black
  1726. labelBlocked.font = UIFont.systemFont(ofSize: 12).bold
  1727. if blocked == "1" {
  1728. labelBlocked.text = "You blocked this user".localized()
  1729. } else {
  1730. labelBlocked.text = "You have been blocked by this user".localized()
  1731. }
  1732. }
  1733. @objc func seeProfileTapped() {
  1734. if dataPerson["f_pin"] == "-999" || dataPerson["isOfficial"] == "1" || removed || copySession || forwardSession || deleteSession || isContactCenter {
  1735. return
  1736. }
  1737. dismissKeyboard()
  1738. let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "profileView") as! ProfileViewController
  1739. controller.data = dataPerson["f_pin"]!!
  1740. controller.checkReadMessage = {
  1741. self.dataPerson.removeAll()
  1742. self.getDataProfile(fPin: self.unique_l_pin)
  1743. self.changeAppBar()
  1744. if self.currentIndexpath == nil {
  1745. var listData = self.dataMessages
  1746. listData = listData.filter({$0["status"] as! String != "4" && $0["status"] as! String != "8"})
  1747. if listData.count != 0 && !self.isContactCenter {
  1748. let idMe = UserDefaults.standard.string(forKey: "me") as String?
  1749. for i in 0...listData.count - 1 {
  1750. if listData[i]["f_pin"] as? String != idMe {
  1751. self.sendReadMessageStatus(chat_id: "", f_pin: self.dataPerson["f_pin"]!!, message_scope_id: "3", message_id: listData[i]["message_id"] as! String)
  1752. }
  1753. }
  1754. }
  1755. } else {
  1756. let dataMessages = self.dataMessages.filter({ $0["chat_date"] as! String == self.dataDates[self.currentIndexpath!.section] })
  1757. var listData = dataMessages
  1758. listData = listData.filter({$0["status"] as! String != "4" && $0["status"] as! String != "8"})
  1759. if listData.count != 0 && !self.isContactCenter {
  1760. let idMe = UserDefaults.standard.string(forKey: "me") as String?
  1761. for i in 0...listData.count - 1 {
  1762. if listData[i]["f_pin"] as? String != idMe {
  1763. self.sendReadMessageStatus(chat_id: "", f_pin: self.dataPerson["f_pin"]!!, message_scope_id: "3", message_id: listData[i]["message_id"] as! String)
  1764. }
  1765. }
  1766. }
  1767. }
  1768. }
  1769. navigationController?.show(controller, sender: nil)
  1770. }
  1771. @IBAction func voiceTapped(_ sender: UIButton) {
  1772. if (self.constraintBottomAttachment.constant != 0.0) {
  1773. constraintBottomAttachment.constant = 0.0
  1774. self.viewSticker.removeConstraints(self.viewSticker.constraints)
  1775. self.viewSticker.removeFromSuperview()
  1776. }
  1777. }
  1778. @IBAction func imageTapped(_ sender: UIButton) {
  1779. if (self.constraintBottomAttachment.constant != 0.0) {
  1780. constraintBottomAttachment.constant = 0.0
  1781. self.viewSticker.removeConstraints(self.viewSticker.constraints)
  1782. self.viewSticker.removeFromSuperview()
  1783. }
  1784. if isContactCenter && fPinContacCenter.isEmpty && isRequestContactCenter {
  1785. return
  1786. }
  1787. let alertController = LibAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
  1788. if let action = self.actionImageVideo(for: "image", title: "Choose Photo".localized()) {
  1789. alertController.addAction(action)
  1790. }
  1791. if let action = self.actionImageVideo(for: "video", title: "Choose Video".localized()) {
  1792. alertController.addAction(action)
  1793. }
  1794. alertController.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: nil))
  1795. self.present(alertController, animated: true)
  1796. }
  1797. private func actionImageVideo(for type: String, title: String) -> UIAlertAction? {
  1798. return UIAlertAction(title: title, style: .default) { [unowned self] _ in
  1799. switch type {
  1800. case "image":
  1801. imageVideoPicker.present(source: .imageAlbum)
  1802. case "video":
  1803. imageVideoPicker.present(source: .videoAlbum)
  1804. case "imageCamera":
  1805. imageVideoPicker.present(source: .imageCamera)
  1806. case "videoCamera":
  1807. imageVideoPicker.present(source: .videoCamera)
  1808. default:
  1809. imageVideoPicker.present(source: .imageAlbum)
  1810. }
  1811. }
  1812. }
  1813. @IBAction func photoTapped(_ sender: UIButton) {
  1814. if (self.constraintBottomAttachment.constant != 0.0) {
  1815. constraintBottomAttachment.constant = 0.0
  1816. self.viewSticker.removeConstraints(self.viewSticker.constraints)
  1817. self.viewSticker.removeFromSuperview()
  1818. }
  1819. if isContactCenter && fPinContacCenter.isEmpty && isRequestContactCenter {
  1820. return
  1821. }
  1822. let alertController = LibAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
  1823. if let action = self.actionImageVideo(for: "imageCamera", title: "Take Photo".localized()) {
  1824. alertController.addAction(action)
  1825. }
  1826. if let action = self.actionImageVideo(for: "videoCamera", title: "Take Video".localized()) {
  1827. alertController.addAction(action)
  1828. }
  1829. alertController.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: nil))
  1830. self.present(alertController, animated: true)
  1831. }
  1832. @IBAction func stickerTapped(_ sender: UIButton) {
  1833. if isContactCenter && fPinContacCenter.isEmpty && isRequestContactCenter {
  1834. return
  1835. }
  1836. DispatchQueue.main.async {
  1837. if (self.constraintBottomAttachment.constant == 0.0) {
  1838. self.constraintBottomAttachment.constant = 200.0
  1839. self.view.addSubview(self.viewSticker)
  1840. self.viewSticker.translatesAutoresizingMaskIntoConstraints = false
  1841. NSLayoutConstraint.activate([
  1842. self.viewSticker.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
  1843. self.viewSticker.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
  1844. self.viewSticker.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
  1845. self.viewSticker.heightAnchor.constraint(equalToConstant: 200)
  1846. ])
  1847. let layout = UICollectionViewFlowLayout()
  1848. layout.scrollDirection = .vertical
  1849. let collectionSticker = UICollectionView(frame: .zero, collectionViewLayout: layout)
  1850. collectionSticker.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cellSticker")
  1851. collectionSticker.delegate = self
  1852. collectionSticker.dataSource = self
  1853. collectionSticker.backgroundColor = .clear
  1854. self.viewSticker.addSubview(collectionSticker)
  1855. collectionSticker.translatesAutoresizingMaskIntoConstraints = false
  1856. NSLayoutConstraint.activate([
  1857. collectionSticker.topAnchor.constraint(equalTo: self.viewSticker.topAnchor, constant: 20),
  1858. collectionSticker.bottomAnchor.constraint(equalTo: self.viewSticker.bottomAnchor, constant: -20),
  1859. collectionSticker.leadingAnchor.constraint(equalTo: self.viewSticker.leadingAnchor, constant: 20),
  1860. collectionSticker.trailingAnchor.constraint(equalTo: self.viewSticker.trailingAnchor, constant: -20)
  1861. ])
  1862. if (self.currentIndexpath != nil) {
  1863. DispatchQueue.main.async {
  1864. self.tableChatView.scrollToRow(at: IndexPath(row: self.currentIndexpath!.row, section: self.currentIndexpath!.section), at: .none, animated: false)
  1865. }
  1866. } else {
  1867. self.tableChatView.scrollToBottom()
  1868. }
  1869. } else {
  1870. self.constraintBottomAttachment.constant = 0.0
  1871. self.viewSticker.removeConstraints(self.viewSticker.constraints)
  1872. self.viewSticker.removeFromSuperview()
  1873. }
  1874. }
  1875. }
  1876. @IBAction func fileTapped(_ sender: UIButton) {
  1877. if isContactCenter && fPinContacCenter.isEmpty && isRequestContactCenter {
  1878. return
  1879. }
  1880. if (self.constraintBottomAttachment.constant != 0.0) {
  1881. constraintBottomAttachment.constant = 0.0
  1882. self.viewSticker.removeConstraints(self.viewSticker.constraints)
  1883. self.viewSticker.removeFromSuperview()
  1884. }
  1885. documentPicker.present()
  1886. }
  1887. @objc func sendTapped() {
  1888. sendChat(message_text: textFieldSend.text!, viewController: self)
  1889. }
  1890. @objc func showChooserACKConfidential() {
  1891. let alertController = LibAlertController(title: "Message Mode".localized(), message: "Select".localized() + " " + "Message Mode".localized(), preferredStyle: .actionSheet)
  1892. let imageConfidential = resizeImage(image: UIImage(named: "confidential_icon", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, targetSize: CGSize(width: 30, height: 30)).withRenderingMode(.alwaysOriginal)
  1893. let imageAck = resizeImage(image: UIImage(named: "ack_icon", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, targetSize: CGSize(width: 30, height: 30)).withRenderingMode(.alwaysOriginal)
  1894. let confidentialAction = UIAlertAction(title: "Confidential Message".localized(), style: .default, handler: { (UIAlertAction) in
  1895. if !self.isConfidential {
  1896. self.isConfidential = true
  1897. self.buttonAckConfidential.setImage(imageConfidential, for: .normal)
  1898. }
  1899. if self.isAck {
  1900. self.isAck = false
  1901. }
  1902. })
  1903. let ackAction = UIAlertAction(title: "Confirmation Message".localized(), style: .default, handler: { (UIAlertAction) in
  1904. if !self.isAck {
  1905. self.isAck = true
  1906. self.buttonAckConfidential.setImage(imageAck, for: .normal)
  1907. }
  1908. if self.isConfidential {
  1909. self.isConfidential = false
  1910. }
  1911. })
  1912. confidentialAction.setValue(imageConfidential, forKey: "image")
  1913. ackAction.setValue(imageAck, forKey: "image")
  1914. alertController.addAction(confidentialAction)
  1915. alertController.addAction(ackAction)
  1916. alertController.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: { (UIAlertAction) in
  1917. self.isConfidential = false
  1918. self.isAck = false
  1919. self.buttonAckConfidential.setImage(UIImage(systemName: "gearshape.fill", withConfiguration: UIImage.SymbolConfiguration(scale: .large))?.withTintColor(.white).withRenderingMode(.alwaysTemplate), for: .normal)
  1920. }))
  1921. self.present(alertController, animated: true, completion: nil)
  1922. }
  1923. public func setAckConfidential(isAck: Bool, isConfidential: Bool) {
  1924. self.isConfidential = isConfidential
  1925. self.isAck = isAck
  1926. let imageConfidential = resizeImage(image: UIImage(named: "confidential_icon", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, targetSize: CGSize(width: 30, height: 30)).withRenderingMode(.alwaysOriginal)
  1927. let imageAck = resizeImage(image: UIImage(named: "ack_icon", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, targetSize: CGSize(width: 30, height: 30)).withRenderingMode(.alwaysOriginal)
  1928. if isAck {
  1929. buttonAckConfidential.setImage(imageAck, for: .normal)
  1930. } else if isConfidential {
  1931. buttonAckConfidential.setImage(imageConfidential, for: .normal)
  1932. } else {
  1933. self.buttonAckConfidential.setImage(UIImage(systemName: "gearshape.fill", withConfiguration: UIImage.SymbolConfiguration(scale: .large))?.withTintColor(.white).withRenderingMode(.alwaysTemplate), for: .normal)
  1934. }
  1935. }
  1936. @objc func addRoom(sender: UIBarButtonItem) {
  1937. let controller = QmeraCallContactViewController()
  1938. controller.isDismiss = { user in
  1939. DispatchQueue.global().async {
  1940. _ = Nexilis.write(message: CoreMessage_TMessageBank.getCCRoomInvite(l_pin: user.pin, ticket_id: self.complaintId, channel: self.channelContactCenter))
  1941. }
  1942. }
  1943. controller.selectedUser.append(contentsOf: users)
  1944. controller.isInviteCC = true
  1945. self.navigationController?.show(controller, sender: nil)
  1946. }
  1947. @objc func audioVideoCall(sender: UIBarButtonItem) {
  1948. if sender.tag == 0 {
  1949. let goAudioCall = Nexilis.checkMicPermission()
  1950. if !goAudioCall{
  1951. let alert = LibAlertController(title: "Attention!".localized(), message: "Please allow microphone permission in your settings".localized(), preferredStyle: .alert)
  1952. alert.addAction(UIAlertAction(title: "OK".localized(), style: UIAlertAction.Style.default, handler: {_ in
  1953. if let url = URL(string: UIApplication.openSettingsURLString), UIApplication.shared.canOpenURL(url) {
  1954. UIApplication.shared.open(url, options: [:], completionHandler: nil)
  1955. }
  1956. }))
  1957. self.navigationController?.present(alert, animated: true, completion: nil)
  1958. return
  1959. }
  1960. if let pin = dataPerson["f_pin"] {
  1961. if !CheckConnection.isConnectedToNetwork() {
  1962. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  1963. imageView.tintColor = .white
  1964. 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)
  1965. banner.show()
  1966. return
  1967. }
  1968. let controller = QmeraAudioViewController()
  1969. controller.user = User.getData(pin: pin)
  1970. controller.isOutgoing = true
  1971. controller.modalPresentationStyle = .overCurrentContext
  1972. present(controller, animated: true, completion: nil)
  1973. }
  1974. } else {
  1975. let goAudioCall = Nexilis.checkMicPermission()
  1976. let goVideoCall = Nexilis.checkCameraPermission()
  1977. if goVideoCall == 0 {
  1978. 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)
  1979. alert.addAction(UIAlertAction(title: "OK".localized(), style: UIAlertAction.Style.default, handler: {_ in
  1980. if let url = URL(string: UIApplication.openSettingsURLString), UIApplication.shared.canOpenURL(url) {
  1981. UIApplication.shared.open(url, options: [:], completionHandler: nil)
  1982. }
  1983. }))
  1984. self.navigationController?.present(alert, animated: true, completion: nil)
  1985. return
  1986. } else if goVideoCall == -1 {
  1987. return
  1988. }
  1989. if !CheckConnection.isConnectedToNetwork() {
  1990. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  1991. imageView.tintColor = .white
  1992. 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)
  1993. banner.show()
  1994. return
  1995. }
  1996. let videoVC = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "videoVCQmera") as! QmeraVideoViewController
  1997. videoVC.dataPerson.append(dataPerson)
  1998. self.show(videoVC, sender: nil)
  1999. }
  2000. }
  2001. @objc func dismissKeyboard() {
  2002. if isSearching {
  2003. searchBar.resignFirstResponder()
  2004. } else {
  2005. textFieldSend.resignFirstResponder() // dismiss keyoard
  2006. if (self.constraintBottomAttachment.constant != 0.0) {
  2007. constraintBottomAttachment.constant = 0.0
  2008. self.viewSticker.removeConstraints(self.viewSticker.constraints)
  2009. self.viewSticker.removeFromSuperview()
  2010. }
  2011. }
  2012. }
  2013. @objc func didTapExit() {
  2014. if complaintId.isEmpty || fromVCAC {
  2015. for timer in self.timerCredential.values {
  2016. timer.invalidate()
  2017. }
  2018. self.timeoutCC.invalidate()
  2019. UserDefaults.standard.removeObject(forKey: "inEditorPersonal")
  2020. NotificationCenter.default.removeObserver(self)
  2021. NotificationCenter.default.post(name: NSNotification.Name(rawValue: "refreshView"), object: nil, userInfo: nil)
  2022. self.dismiss(animated: true, completion: nil)
  2023. } else if !complaintId.isEmpty {
  2024. 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)
  2025. alert.addAction(UIAlertAction(title: "No".localized(), style: UIAlertAction.Style.default, handler: nil))
  2026. alert.addAction(UIAlertAction(title: "Yes".localized(), style: UIAlertAction.Style.default, handler: {(_) in
  2027. self.endCallCenter()
  2028. }))
  2029. self.present(alert, animated: true, completion: nil)
  2030. }
  2031. }
  2032. public func endCallCenter() {
  2033. timeoutCC.invalidate()
  2034. let complaintId = self.complaintId
  2035. let idMe = UserDefaults.standard.string(forKey: "me")!
  2036. let onGoingCC = UserDefaults.standard.string(forKey: "onGoingCC") ?? ""
  2037. let requester = onGoingCC.components(separatedBy: ",")[0]
  2038. let officer = onGoingCC.isEmpty ? "" : onGoingCC.components(separatedBy: ",")[1]
  2039. DispatchQueue.global().async {
  2040. let date = "\(Date().currentTimeMillis())"
  2041. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  2042. do {
  2043. _ = try Database.shared.insertRecord(fmdb: fmdb, table: "CALL_CENTER_HISTORY", cvalues: [
  2044. "type" : self.channelContactCenter,
  2045. "title" : "Contact Center".localized(),
  2046. "time" : self.dateStartCC,
  2047. "f_pin" : officer,
  2048. "data" : self.complaintId,
  2049. "time_end" : date,
  2050. "complaint_id" : self.complaintId,
  2051. "members" : "",
  2052. "requester": requester
  2053. ], replace: true)
  2054. } catch {
  2055. rollback.pointee = true
  2056. //print(error)
  2057. }
  2058. })
  2059. if officer == idMe {
  2060. _ = Nexilis.write(message: CoreMessage_TMessageBank.endCallCenter(complaint_id: complaintId, l_pin: requester))
  2061. } else {
  2062. if requester == idMe {
  2063. _ = Nexilis.write(message: CoreMessage_TMessageBank.endCallCenter(complaint_id: complaintId, l_pin: officer))
  2064. } else {
  2065. _ = Nexilis.write(message: CoreMessage_TMessageBank.leaveCCRoomInvite(ticket_id: complaintId))
  2066. }
  2067. }
  2068. UserDefaults.standard.removeObject(forKey: "onGoingCC")
  2069. UserDefaults.standard.removeObject(forKey: "membersCC")
  2070. UserDefaults.standard.removeObject(forKey: "waitingRequestCC")
  2071. }
  2072. self.dismiss(animated: true, completion: nil)
  2073. }
  2074. @objc func keyboardWillHide(notification: NSNotification) {
  2075. if self.viewIfLoaded?.window != nil {
  2076. let info:NSDictionary = notification.userInfo! as NSDictionary
  2077. let duration: CGFloat = info[UIResponder.keyboardAnimationDurationUserInfoKey] as! NSNumber as! CGFloat
  2078. self.constraintViewTextField.constant = 0
  2079. self.constraintBottomContainerMultpileSelectSession.constant = 0
  2080. UIView.animate(withDuration: TimeInterval(duration), animations: {
  2081. self.view.layoutIfNeeded()
  2082. })
  2083. }
  2084. }
  2085. @objc func keyboardWillShow(notification: NSNotification) {
  2086. if self.viewIfLoaded?.window != nil {
  2087. if (self.constraintBottomAttachment.constant != 0.0) {
  2088. self.constraintBottomAttachment.constant = 0.0
  2089. self.viewSticker.removeConstraints(self.viewSticker.constraints)
  2090. self.viewSticker.removeFromSuperview()
  2091. }
  2092. let info:NSDictionary = notification.userInfo! as NSDictionary
  2093. let keyboardSize = (info[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
  2094. let keyboardHeight: CGFloat = keyboardSize.height
  2095. let duration: CGFloat = info[UIResponder.keyboardAnimationDurationUserInfoKey] as! NSNumber as! CGFloat
  2096. if self.constraintViewTextField.constant != keyboardHeight - 60 {
  2097. self.constraintViewTextField.constant = keyboardHeight - 60
  2098. if isSearching {
  2099. self.constraintViewTextField.constant = self.constraintViewTextField.constant + 60
  2100. self.constraintBottomContainerMultpileSelectSession.constant = -keyboardHeight
  2101. }
  2102. UIView.animate(withDuration: TimeInterval(duration), animations: {
  2103. self.view.layoutIfNeeded()
  2104. })
  2105. if isSearching {
  2106. self.tableChatView.scrollToBottom()
  2107. } else {
  2108. if (self.currentIndexpath != nil) {
  2109. self.tableChatView.scrollToRow(at: IndexPath(row: self.currentIndexpath!.row, section: self.currentIndexpath!.section), at: .none, animated: false)
  2110. } else {
  2111. self.tableChatView.scrollToBottom()
  2112. }
  2113. }
  2114. }
  2115. }
  2116. }
  2117. private func sendChat(message_scope_id:String = "3", status:String = "3", 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) {
  2118. if viewController is EditorPersonal && file_id == "" && dataMessageForward == nil && !isAutoSendCC{
  2119. if ((textFieldSend.text!.trimmingCharacters(in: .whitespacesAndNewlines) == "Send message".localized() && textFieldSend.textColor == UIColor.lightGray && attachment_flag != "11") || textFieldSend.text!.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ) {
  2120. dismissKeyboard()
  2121. viewController.showToast(message: "Write Messages".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
  2122. if (textFieldSend.text!.trimmingCharacters(in: .whitespacesAndNewlines) != "Send message".localized()) {
  2123. textFieldSend.text = ""
  2124. }
  2125. if (self.heightTextFieldSend.constant != 40) {
  2126. self.heightTextFieldSend.constant = 40
  2127. }
  2128. return
  2129. }
  2130. }
  2131. var reff_id = reff_id
  2132. if (reffId != nil) {
  2133. reff_id = reffId!
  2134. }
  2135. var is_call_center = is_call_center
  2136. var call_center_id = call_center_id
  2137. var l_pin = dataPerson["f_pin"]!!
  2138. var message_scope_id = message_scope_id
  2139. var chat_id = chat_id
  2140. if (isContactCenter) {
  2141. if fPinContacCenter.isEmpty && isRequestContactCenter {
  2142. if textFieldSend.text!.trimmingCharacters(in: .whitespacesAndNewlines) != "Send message".localized() && textFieldSend.textColor != UIColor.lightGray && constraintViewTextField.constant == 0 {
  2143. textFieldSend.text = "Send message".localized()
  2144. textFieldSend.textColor = UIColor.lightGray
  2145. } else if constraintViewTextField.constant != 0 {
  2146. textFieldSend.text = ""
  2147. }
  2148. dismissKeyboard()
  2149. viewController.showToast(message: "Unable to send message. Waiting for the officer to accept your request".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
  2150. return
  2151. }
  2152. is_call_center = "1"
  2153. call_center_id = complaintId
  2154. l_pin = fPinContacCenter
  2155. message_scope_id = "5"
  2156. chat_id = complaintId
  2157. if isAutoSendCC {
  2158. timeoutCC = Timer.scheduledTimer(withTimeInterval: 30.0, repeats: false, block: {_ in
  2159. let imageView = UIImageView(image: UIImage(systemName: "info.circle"))
  2160. imageView.tintColor = .white
  2161. 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)
  2162. banner.show()
  2163. self.endCallCenter()
  2164. })
  2165. }
  2166. }
  2167. let message_text = message_text.trimmingCharacters(in: .whitespacesAndNewlines)
  2168. let idMe = UserDefaults.standard.string(forKey: "me") as String?
  2169. var opposite_pin = ""
  2170. if isContactCenter {
  2171. opposite_pin = ""
  2172. } else {
  2173. opposite_pin = idMe ?? ""
  2174. }
  2175. var credential = credential
  2176. if isConfidential {
  2177. credential = "1"
  2178. }
  2179. var read_receipts = read_receipts
  2180. if isAck {
  2181. read_receipts = "8"
  2182. }
  2183. sendTyping(l_pin: l_pin, isTyping: true)
  2184. 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)
  2185. Nexilis.addQueueMessage(message: message)
  2186. let messageId = String(message.mBodies[CoreMessage_TMessageKey.MESSAGE_ID]!)
  2187. if credential == "1" {
  2188. self.listTimerCredential[messageId] = 60
  2189. }
  2190. var row: [String: Any?] = [:]
  2191. row["message_id"] = messageId
  2192. row["f_pin"] = idMe
  2193. row["l_pin"] = dataPerson["f_pin"]!!
  2194. row["message_scope_id"] = message_scope_id
  2195. row["server_date"] = "\(Date().currentTimeMillis())"
  2196. row["status"] = status
  2197. row["message_text"] = message_text
  2198. row["audio_id"] = audio_id
  2199. row["video_id"] = video_id
  2200. row["image_id"] = image_id
  2201. row["thumb_id"] = thumb_id
  2202. row["read_receipts"] = read_receipts
  2203. row["credential"] = credential
  2204. row["chat_id"] = chat_id
  2205. row["file_id"] = file_id
  2206. row["attachment_flag"] = attachment_flag
  2207. row["reff_id"] = reff_id
  2208. row["progress"] = 0.0
  2209. row["lock"] = "0"
  2210. row["is_stared"] = "0"
  2211. row["isSelected"] = false
  2212. if !dataDates.contains("Today".localized()) {
  2213. dataDates.append("Today".localized())
  2214. tableChatView.insertSections(IndexSet(integer: dataDates.count - 1), with: .none)
  2215. }
  2216. row["chat_date"] = "Today".localized()
  2217. dataMessages.append(row)
  2218. tableChatView.insertRows(at: [IndexPath(row: dataMessages.filter({ $0["chat_date"] as! String == dataDates[dataDates.count - 1]}).count - 1, section: dataDates.count - 1)], with: .none)
  2219. if credential == "1" {
  2220. var timer = Timer()
  2221. var minute = 60
  2222. self.timerCredential[messageId] = timer
  2223. timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: {_ in
  2224. minute -= 1
  2225. self.listTimerCredential[messageId] = minute
  2226. if minute == 0 {
  2227. timer.invalidate()
  2228. self.listTimerCredential.removeValue(forKey: messageId)
  2229. self.timerCredential.removeValue(forKey: messageId)
  2230. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == messageId})
  2231. if idx != nil {
  2232. self.dataMessages[idx!]["lock"] = "2"
  2233. self.dataMessages[idx!]["reff_id"] = ""
  2234. }
  2235. DispatchQueue.global().async {
  2236. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  2237. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  2238. "lock" : "2"
  2239. ], _where: "message_id = '\(messageId)'")
  2240. })
  2241. }
  2242. }
  2243. self.tableChatView.reloadRows(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)
  2244. })
  2245. }
  2246. if textFieldSend.text!.trimmingCharacters(in: .whitespacesAndNewlines) != "Send message".localized() && textFieldSend.textColor != UIColor.lightGray && constraintViewTextField.constant == 0 {
  2247. textFieldSend.text = "Send message".localized()
  2248. textFieldSend.textColor = UIColor.lightGray
  2249. } else if constraintViewTextField.constant != 0 {
  2250. textFieldSend.text = ""
  2251. heightTextFieldSend.constant = 40
  2252. }
  2253. deleteReplyView()
  2254. deleteLinkPreview()
  2255. DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
  2256. self.tableChatView.scrollToBottom()
  2257. if self.markerCounter != nil {
  2258. let lastMarkerCounter = self.markerCounter
  2259. self.markerCounter = nil
  2260. self.tableChatView.beginUpdates()
  2261. let indexMessage = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == lastMarkerCounter })
  2262. if indexMessage != nil {
  2263. let section = self.dataDates.firstIndex(of: self.dataMessages[indexMessage!]["chat_date"] as! String)
  2264. 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 })
  2265. if row != nil && section != nil {
  2266. self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
  2267. }
  2268. }
  2269. self.tableChatView.endUpdates()
  2270. }
  2271. }
  2272. }
  2273. @objc func ccAction(sender: UIButton) {
  2274. if self.nowSelectedCategoryCC == "CantReturn" {
  2275. if sender.tag == 503 {
  2276. self.showToast(message: "You can't request Call Center more than one".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
  2277. } else if sender.tag == 504 {
  2278. busyCCAction(sender: sender)
  2279. }
  2280. return
  2281. }
  2282. if self.nowSelectedCategoryCC == "endCC" {
  2283. return
  2284. }
  2285. let id = sender.restorationIdentifier?.components(separatedBy: ",")[0]
  2286. let service_id = sender.restorationIdentifier?.components(separatedBy: ",")[1]
  2287. let level = id!.substring(from: 5, to: 5)
  2288. let levelNow = self.nowSelectedCategoryCC.substring(from: 5, to: 5)
  2289. var isRequest = false
  2290. var channel = 0
  2291. var row: [String: Any?] = [:]
  2292. if nowSelectedCategoryCC.isEmpty || level > levelNow {
  2293. if Utils.getDefaultCC() == "No" && !showToastTwiceClick {
  2294. self.showToast(message: "You can press your choice again to change category".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
  2295. showToastTwiceClick = true
  2296. }
  2297. row["message_id"] = ""
  2298. row["chat_date"] = "Today".localized()
  2299. let dataChat: [CategoryCC] = CategoryCC.getDatafromParent(parent: service_id!)
  2300. if dataChat.count != 0 {
  2301. var data : [CategoryCC] = []
  2302. for i in 0..<dataChat.count {
  2303. 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))
  2304. }
  2305. row["category_cc"] = data
  2306. } else if dataMessages[Int(level)!]["attachment_flag"] == nil {
  2307. let listStringName: [String] = ["Informasi Umum Produk Call 1500046", "Informasi Spesifik Produk"]
  2308. var data : [CategoryCC] = []
  2309. for i in 0..<listStringName.count {
  2310. data.append(CategoryCC(id: "level\(Int(level)! + 1)_\(i)", service_id: service_id!, service_name: listStringName[i], parent: id!, description: "", is_tablet: "0"))
  2311. }
  2312. row["category_cc"] = data
  2313. row["attachment_flag"] = "502"
  2314. } else if dataMessages[Int(level)!]["attachment_flag"] != nil && dataMessages[Int(level)!]["attachment_flag"] as! String == "502" {
  2315. if id == "level\(Int(level)!)_0" {
  2316. if let url = URL(string: "tel://1500046") {
  2317. UIApplication.shared.open(url)
  2318. }
  2319. return
  2320. } else {
  2321. let listStringName: [String] = ["Chat with a Representative".localized(), "Video Call a Representative".localized(), "Call a Representative".localized()]
  2322. var data : [CategoryCC] = []
  2323. for i in 0..<listStringName.count {
  2324. data.append(CategoryCC(id: "level\(Int(level)! + 1)_\(i)", service_id: service_id!, service_name: listStringName[i], parent: id!, description: "", is_tablet: "0"))
  2325. }
  2326. row["category_cc"] = data
  2327. row["attachment_flag"] = "503"
  2328. }
  2329. } else {
  2330. if id == "level\(Int(level)!)_1" {
  2331. channel = 2
  2332. } else if id == "level\(Int(level)!)_2" {
  2333. channel = 1
  2334. }
  2335. if channel == 1 || channel == 2 {
  2336. if channel == 2 {
  2337. let goAudioCall = Nexilis.checkMicPermission()
  2338. let goVideoCall = Nexilis.checkCameraPermission()
  2339. if goVideoCall == 0 {
  2340. 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)
  2341. alert.addAction(UIAlertAction(title: "OK".localized(), style: UIAlertAction.Style.default, handler: {_ in
  2342. if let url = URL(string: UIApplication.openSettingsURLString), UIApplication.shared.canOpenURL(url) {
  2343. UIApplication.shared.open(url, options: [:], completionHandler: nil)
  2344. }
  2345. }))
  2346. self.navigationController?.present(alert, animated: true, completion: nil)
  2347. return
  2348. } else if goVideoCall == -1 {
  2349. return
  2350. }
  2351. } else if channel == 1 {
  2352. let goAudioCall = Nexilis.checkMicPermission()
  2353. if !goAudioCall{
  2354. let alert = LibAlertController(title: "Attention!".localized(), message: "Please allow microphone permission in your settings".localized(), preferredStyle: .alert)
  2355. alert.addAction(UIAlertAction(title: "OK".localized(), style: UIAlertAction.Style.default, handler: {_ in
  2356. if let url = URL(string: UIApplication.openSettingsURLString), UIApplication.shared.canOpenURL(url) {
  2357. UIApplication.shared.open(url, options: [:], completionHandler: nil)
  2358. }
  2359. }))
  2360. self.navigationController?.present(alert, animated: true, completion: nil)
  2361. return
  2362. }
  2363. }
  2364. }
  2365. row["category_cc"] = "Please wait while we connect you\nto one of our service representatives".localized()
  2366. isRequest = true
  2367. }
  2368. if dataMessages[Int(level)!]["attachment_flag"] == nil || dataMessages[Int(level)!]["attachment_flag"] as! String != "503" {
  2369. dataMessages.append(row)
  2370. self.nowSelectedCategoryCC = id!
  2371. tableChatView.insertRows(at: [IndexPath(row: dataMessages.count - 1, section: 0)], with: .none)
  2372. }
  2373. } else {
  2374. if id == self.nowSelectedCategoryCC {
  2375. if level == "0" {
  2376. self.nowSelectedCategoryCC = ""
  2377. } else {
  2378. let categoryCC = dataMessages[dataMessages.count - 2]["category_cc"] as! [CategoryCC]
  2379. self.nowSelectedCategoryCC = categoryCC[0].parent
  2380. }
  2381. tableChatView.beginUpdates()
  2382. tableChatView.deleteRows(at: [IndexPath(row: dataMessages.count - 1, section: 0)], with: .none)
  2383. dataMessages.remove(at: dataMessages.count - 1)
  2384. tableChatView.endUpdates()
  2385. } else {
  2386. return
  2387. }
  2388. }
  2389. if Utils.getDefaultCC() == "No" {
  2390. if sender.backgroundColor != .orangeBNI {
  2391. var button = dataMessages[dataMessages.count - 2]["category_cc"] as! [CategoryCC]
  2392. if dataMessages[Int(level)!]["attachment_flag"] != nil && dataMessages[Int(level)!]["attachment_flag"] as! String == "503" {
  2393. button = dataMessages[dataMessages.count - 1]["category_cc"] as! [CategoryCC]
  2394. }
  2395. for i in button {
  2396. if i.id == id! {
  2397. i.isActive = true
  2398. break
  2399. }
  2400. }
  2401. sender.backgroundColor = .orangeBNI
  2402. dataMessages[dataMessages.count - 2]["category_cc"] = button
  2403. } else {
  2404. let button = dataMessages[dataMessages.count - 1]["category_cc"] as! [CategoryCC]
  2405. for i in button {
  2406. if i.id == id! {
  2407. i.isActive = false
  2408. break
  2409. }
  2410. }
  2411. sender.backgroundColor = .clear
  2412. dataMessages[dataMessages.count - 1]["category_cc"] = button
  2413. }
  2414. }
  2415. if isRequest {
  2416. requestContactCenter(channel: channel, service_id: service_id!, row: row)
  2417. } else {
  2418. self.tableChatView.scrollToBottom()
  2419. }
  2420. }
  2421. private func directCC() {
  2422. if channelContactCenter == "1" || channelContactCenter == "2" {
  2423. if channelContactCenter == "2" {
  2424. let goAudioCall = Nexilis.checkMicPermission()
  2425. let goVideoCall = Nexilis.checkCameraPermission()
  2426. if goVideoCall == 0 {
  2427. 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)
  2428. alert.addAction(UIAlertAction(title: "OK".localized(), style: UIAlertAction.Style.default, handler: {_ in
  2429. if let url = URL(string: UIApplication.openSettingsURLString), UIApplication.shared.canOpenURL(url) {
  2430. UIApplication.shared.open(url, options: [:], completionHandler: nil)
  2431. }
  2432. }))
  2433. self.navigationController?.present(alert, animated: true, completion: nil)
  2434. return
  2435. } else if goVideoCall == -1 {
  2436. return
  2437. }
  2438. } else if channelContactCenter == "1" {
  2439. let goAudioCall = Nexilis.checkMicPermission()
  2440. if !goAudioCall{
  2441. let alert = LibAlertController(title: "Attention!".localized(), message: "Please allow microphone permission in your settings".localized(), preferredStyle: .alert)
  2442. alert.addAction(UIAlertAction(title: "OK".localized(), style: UIAlertAction.Style.default, handler: {_ in
  2443. if let url = URL(string: UIApplication.openSettingsURLString), UIApplication.shared.canOpenURL(url) {
  2444. UIApplication.shared.open(url, options: [:], completionHandler: nil)
  2445. }
  2446. }))
  2447. self.navigationController?.present(alert, animated: true, completion: nil)
  2448. return
  2449. }
  2450. }
  2451. }
  2452. var row: [String: Any?] = [:]
  2453. row["message_id"] = ""
  2454. row["chat_date"] = "Today".localized()
  2455. row["category_cc"] = "Please wait while we connect you\nto one of our service representatives".localized()
  2456. dataMessages.append(row)
  2457. nowSelectedCategoryCC = "CantReturn"
  2458. tableChatView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .none)
  2459. requestContactCenter(channel: Int(channelContactCenter)!, service_id: serviceIdCC, row: row)
  2460. }
  2461. @objc func busyCCAction(sender: UIButton) {
  2462. let id = sender.restorationIdentifier?.components(separatedBy: ",")[0]
  2463. let service_id = sender.restorationIdentifier?.components(separatedBy: ",")[1]
  2464. let level = id!.substring(from: 5, to: 5)
  2465. var row: [String: Any?] = [:]
  2466. if id == "level\(Int(level)!)_0" {
  2467. UserDefaults.standard.set(true, forKey: "waitingRequestCC")
  2468. DispatchQueue.global().async {
  2469. let message = CoreMessage_TMessageBank.getQueuingCallCenter(p_channel: Int(self.channelContactCenter)!)
  2470. message.mBodies[CoreMessage_TMessageKey.CATEGORY_ID] = "\(service_id!)"
  2471. _ = Nexilis.writeSync(message: message, timeout: 30 * 1000)
  2472. }
  2473. row["category_cc"] = "Thank you for contacting us,\none of our officers will contact you soon".localized()
  2474. } else {
  2475. row["category_cc"] = "Thank you for being awesome,\nhave a great day!".localized()
  2476. }
  2477. row["message_id"] = ""
  2478. row["chat_date"] = "Today".localized()
  2479. self.nowSelectedCategoryCC = "endCC"
  2480. dataMessages.append(row)
  2481. tableChatView.insertRows(at: [IndexPath(row: Int(level)!, section: 0)], with: .none)
  2482. self.tableChatView.scrollToBottom()
  2483. // DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {
  2484. // self.dismiss(animated: true)
  2485. // })
  2486. }
  2487. func requestContactCenter(channel: Int, service_id: String, row: [String: Any?]) {
  2488. if !CheckConnection.isConnectedToNetwork() || API.nGetCLXConnState() == 0 {
  2489. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  2490. imageView.tintColor = .white
  2491. 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)
  2492. banner.show()
  2493. return
  2494. }
  2495. DispatchQueue.global().async {
  2496. let message = CoreMessage_TMessageBank.getRequestCallCenter(p_channel: channel, category_id: service_id)
  2497. if let response = Nexilis.writeSync(message: message) {
  2498. if !self.isDirectCC {
  2499. DispatchQueue.main.async {
  2500. self.dataMessages.append(row)
  2501. self.nowSelectedCategoryCC = "CantReturn"
  2502. self.tableChatView.insertRows(at: [IndexPath(row: self.dataMessages.count - 1, section: 0)], with: .none)
  2503. self.tableChatView.scrollToBottom()
  2504. }
  2505. }
  2506. if (response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "00") {
  2507. DispatchQueue.main.async {
  2508. UserDefaults.standard.set(true, forKey: "waitingRequestCC")
  2509. let data = response.getBody(key: CoreMessage_TMessageKey.DATA, default_value: "")
  2510. if data.isEmpty {
  2511. UserDefaults.standard.removeObject(forKey: "waitingRequestCC")
  2512. var row: [String: Any?] = [:]
  2513. row["message_id"] = ""
  2514. row["chat_date"] = "Today".localized()
  2515. row["attachment_flag"] = "504"
  2516. let listStringName: [String] = ["Yes".localized(), "No".localized()]
  2517. var data : [CategoryCC] = []
  2518. for i in 0..<listStringName.count {
  2519. data.append(CategoryCC(id: "level\(self.dataMessages.count + 1)_\(i)", service_id: service_id, service_name: listStringName[i], parent: "", description: "", is_tablet: "0"))
  2520. }
  2521. row["category_cc"] = data
  2522. self.dataMessages.append(row)
  2523. self.channelContactCenter = "\(channel)"
  2524. self.tableChatView.insertRows(at: [IndexPath(row: self.dataMessages.count - 1, section: 0)], with: .none)
  2525. self.tableChatView.scrollToBottom()
  2526. } else {
  2527. self.fPinContacCenter = data
  2528. }
  2529. }
  2530. } else {
  2531. DispatchQueue.main.async {
  2532. UserDefaults.standard.removeObject(forKey: "waitingRequestCC")
  2533. var row: [String: Any?] = [:]
  2534. row["message_id"] = ""
  2535. row["chat_date"] = "Today".localized()
  2536. row["attachment_flag"] = "504"
  2537. let listStringName: [String] = ["Yes".localized(), "No".localized()]
  2538. var data : [CategoryCC] = []
  2539. for i in 0..<listStringName.count {
  2540. data.append(CategoryCC(id: "level\(self.dataMessages.count + 1)_\(i)", service_id: service_id, service_name: listStringName[i], parent: "", description: "", is_tablet: "0"))
  2541. }
  2542. row["category_cc"] = data
  2543. self.dataMessages.append(row)
  2544. self.channelContactCenter = "\(channel)"
  2545. self.tableChatView.insertRows(at: [IndexPath(row: self.dataMessages.count - 1, section: 0)], with: .none)
  2546. self.tableChatView.scrollToBottom()
  2547. }
  2548. }
  2549. }
  2550. }
  2551. }
  2552. private func sendReadMessageStatus(chat_id: String, f_pin: String, message_scope_id: String, message_id: String) {
  2553. let message = CoreMessage_TMessageBank.getUpdateRead(p_chat_id: chat_id, p_f_pin: f_pin, p_scope_id: message_scope_id, qty: 1)
  2554. let fPin = message.getBody(key: CoreMessage_TMessageKey.F_PIN)
  2555. let scope = message.getBody(key: CoreMessage_TMessageKey.SCOPE_ID)
  2556. message.mBodies[CoreMessage_TMessageKey.SERVER_DATE] = String(Date().currentTimeMillis())
  2557. if (fPin.elementsEqual("-999") || scope.elementsEqual("16") || scope.elementsEqual("15")){
  2558. return
  2559. }
  2560. DispatchQueue.global().async {
  2561. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  2562. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  2563. "status" : "4"
  2564. ], _where: "message_id = '\(message_id)'")
  2565. })
  2566. message.mStatus = CoreMessage_TMessageUtil.getTID()
  2567. message.mBodies[CoreMessage_TMessageKey.L_PIN] = f_pin
  2568. message.mBodies[CoreMessage_TMessageKey.MESSAGE_ID] = "-2,\(message_id)"
  2569. _ = Nexilis.write(message: message)
  2570. }
  2571. if let index = dataMessages.firstIndex(where: {$0["message_id"] as? String == message_id}) {
  2572. dataMessages[index]["status"] = "4"
  2573. let auto = UserDefaults.standard.bool(forKey: "autoDownload")
  2574. if auto {
  2575. if dataMessages[index]["image_id"] as? String != nil && !((dataMessages[index]["image_id"] as? String)!.isEmpty) {
  2576. Download().start(forKey:dataMessages[index]["image_id"] as! String) { (name, progress) in
  2577. guard progress == 100 else {
  2578. return
  2579. }
  2580. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  2581. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  2582. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  2583. if let dirPath = paths.first {
  2584. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(self.dataMessages[index]["image_id"] as! String)
  2585. let image = UIImage(contentsOfFile: imageURL.path)
  2586. let save = UserDefaults.standard.bool(forKey: "saveToGallery")
  2587. if save {
  2588. UIImageWriteToSavedPhotosAlbum(image!, nil, nil, nil)
  2589. }
  2590. }
  2591. DispatchQueue.main.async { [self] in
  2592. let section = dataDates.firstIndex(of: dataMessages[index]["chat_date"] as! String)
  2593. let row = dataMessages.filter({$0["chat_date"] as! String == dataMessages[index]["chat_date"] as! String}).firstIndex(where: { $0["message_id"] as? String == message_id})
  2594. if row != nil && section != nil{
  2595. tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
  2596. }
  2597. }
  2598. }
  2599. } else if dataMessages[index]["video_id"] as? String != nil && !((dataMessages[index]["video_id"] as? String)!.isEmpty){
  2600. let section = dataDates.firstIndex(of: dataMessages[index]["chat_date"] as! String)
  2601. let row = dataMessages.filter({$0["chat_date"] as! String == dataMessages[index]["chat_date"] as! String}).firstIndex(where: { $0["message_id"] as? String == message_id})
  2602. if row != nil && section != nil{
  2603. let indexPath = IndexPath(row: row!, section: section!)
  2604. if let cell = tableChatView.cellForRow(at: indexPath) {
  2605. for view in cell.contentView.subviews {
  2606. if view is UIImageView{
  2607. let objectTap = ObjectGesture()
  2608. objectTap.video_id = dataMessages[index]["video_id"] as! String
  2609. objectTap.imageView = view as! UIImageView
  2610. objectTap.indexPath = indexPath
  2611. contentMessageTapped(objectTap)
  2612. break
  2613. }
  2614. }
  2615. }
  2616. }
  2617. }
  2618. }
  2619. }
  2620. }
  2621. private func sendTyping(l_pin: String, isTyping: Bool = false) {
  2622. DispatchQueue.global().async {
  2623. let tmessage = CoreMessage_TMessageBank.getUpdateTypingStatus(p_opposite: l_pin, p_scope: "3", p_status: isTyping ? "3": "4")
  2624. _ = Nexilis.write(message: tmessage)
  2625. }
  2626. }
  2627. private func getCounter() {
  2628. Database().database?.inTransaction({ fmdb, rollback in
  2629. if let c = Database().getRecords(fmdb: fmdb, query: "SELECT counter FROM MESSAGE_SUMMARY where l_pin='\(dataPerson["f_pin"]!!)'"), c.next() {
  2630. counter = Int(c.int(forColumnIndex: 0))
  2631. c.close()
  2632. }
  2633. })
  2634. }
  2635. private func updateCounter(counter: Int) {
  2636. DispatchQueue.global().async {
  2637. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  2638. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE_SUMMARY", cvalues: [
  2639. "counter" : "\(counter)"
  2640. ], _where: "l_pin = '\(self.dataPerson["f_pin"]!!)'")
  2641. })
  2642. NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
  2643. }
  2644. }
  2645. private func addButtonScrollToBottom() {
  2646. self.view.addSubview(buttonScrollToBottom)
  2647. buttonScrollToBottom.translatesAutoresizingMaskIntoConstraints = false
  2648. NSLayoutConstraint.activate([
  2649. buttonScrollToBottom.bottomAnchor.constraint(equalTo: buttonSendChat.topAnchor, constant: -50),
  2650. buttonScrollToBottom.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
  2651. buttonScrollToBottom.widthAnchor.constraint(equalToConstant: 60),
  2652. buttonScrollToBottom.heightAnchor.constraint(equalToConstant: 30.0)
  2653. ])
  2654. buttonScrollToBottom.backgroundColor = .greenColor
  2655. buttonScrollToBottom.setImage(UIImage(systemName: "chevron.down.circle"), for: .normal)
  2656. buttonScrollToBottom.imageView?.contentMode = .scaleAspectFit
  2657. buttonScrollToBottom.imageView?.tintColor = .white
  2658. buttonScrollToBottom.contentVerticalAlignment = .fill
  2659. buttonScrollToBottom.contentHorizontalAlignment = .fill
  2660. buttonScrollToBottom.imageEdgeInsets.top = 2.0
  2661. buttonScrollToBottom.imageEdgeInsets.bottom = 2.0
  2662. buttonScrollToBottom.layer.cornerRadius = 10.0
  2663. buttonScrollToBottom.layer.maskedCorners = [.layerMinXMinYCorner, .layerMinXMaxYCorner]
  2664. buttonScrollToBottom.clipsToBounds = true
  2665. buttonScrollToBottom.addTarget(self, action: #selector(scrollTobottomAction), for: .touchUpInside)
  2666. }
  2667. private func addCounterAtButttonScrollToBottom() {
  2668. self.view.addSubview(indicatorCounterBSTB)
  2669. indicatorCounterBSTB.translatesAutoresizingMaskIntoConstraints = false
  2670. indicatorCounterBSTB.backgroundColor = .systemRed
  2671. indicatorCounterBSTB.layer.cornerRadius = 7.5
  2672. indicatorCounterBSTB.clipsToBounds = true
  2673. indicatorCounterBSTB.layer.borderWidth = 0.5
  2674. indicatorCounterBSTB.layer.borderColor = UIColor.secondaryColor.cgColor
  2675. NSLayoutConstraint.activate([
  2676. indicatorCounterBSTB.bottomAnchor.constraint(equalTo: buttonScrollToBottom.topAnchor, constant: 5),
  2677. indicatorCounterBSTB.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -50),
  2678. indicatorCounterBSTB.widthAnchor.constraint(greaterThanOrEqualToConstant: 15),
  2679. indicatorCounterBSTB.heightAnchor.constraint(equalToConstant: 15)
  2680. ])
  2681. indicatorCounterBSTB.addSubview(labelCounter)
  2682. labelCounter.translatesAutoresizingMaskIntoConstraints = false
  2683. NSLayoutConstraint.activate([
  2684. labelCounter.leadingAnchor.constraint(equalTo: indicatorCounterBSTB.leadingAnchor, constant: 2),
  2685. labelCounter.trailingAnchor.constraint(equalTo: indicatorCounterBSTB.trailingAnchor, constant: -2),
  2686. labelCounter.centerXAnchor.constraint(equalTo: indicatorCounterBSTB.centerXAnchor),
  2687. ])
  2688. labelCounter.font = UIFont.systemFont(ofSize: 11)
  2689. labelCounter.text = "\(counter)"
  2690. labelCounter.textColor = .secondaryColor
  2691. labelCounter.textAlignment = .center
  2692. }
  2693. @objc func scrollTobottomAction() {
  2694. tableChatView.scrollToBottom()
  2695. DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) { [self] in
  2696. if buttonScrollToBottom.isDescendant(of: self.view) {
  2697. buttonScrollToBottom.removeConstraints(buttonScrollToBottom.constraints)
  2698. buttonScrollToBottom.removeFromSuperview()
  2699. if indicatorCounterBSTB.isDescendant(of: self.view) {
  2700. indicatorCounterBSTB.removeConstraints(indicatorCounterBSTB.constraints)
  2701. indicatorCounterBSTB.removeFromSuperview()
  2702. }
  2703. }
  2704. }
  2705. }
  2706. private func checkNewMessage(tableView: UITableView) {
  2707. // let indexPathFirst = tableView.indexPathsForVisibleRows?.first
  2708. // if indexPathFirst != nil {
  2709. // let dataMessages = self.dataMessages.filter({ $0["chat_date"] as! String == dataDates[indexPathFirst!.section] })
  2710. // if self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[indexPathFirst!.row]["message_id"] as? String }) == 0 && !gettingDataMessage {
  2711. // gettingDataMessage = true
  2712. // addDataMessage()
  2713. // }
  2714. // }
  2715. currentIndexpath = tableView.indexPathsForVisibleRows?.last
  2716. if currentIndexpath != nil {
  2717. let dataMessages = dataMessages.filter({ $0["chat_date"] as! String == dataDates[currentIndexpath!.section] })
  2718. if dataMessages.count == 0 || dataMessages.count - 1 < currentIndexpath!.row {
  2719. return
  2720. }
  2721. if currentIndexpath!.section == dataDates.count - 1 && currentIndexpath!.row != dataMessages.count - 1 && currentIndexpath!.row != dataMessages.count - 2 && !buttonScrollToBottom.isDescendant(of: self.view) {
  2722. addButtonScrollToBottom()
  2723. addCounterAtButttonScrollToBottom()
  2724. } else if currentIndexpath!.section == dataDates.count - 1 && currentIndexpath!.row == dataMessages.count - 1 {
  2725. if buttonScrollToBottom.isDescendant(of: self.view) {
  2726. buttonScrollToBottom.removeConstraints(buttonScrollToBottom.constraints)
  2727. buttonScrollToBottom.removeFromSuperview()
  2728. if indicatorCounterBSTB.isDescendant(of: self.view) {
  2729. indicatorCounterBSTB.removeConstraints(indicatorCounterBSTB.constraints)
  2730. indicatorCounterBSTB.removeFromSuperview()
  2731. }
  2732. }
  2733. }
  2734. let indexPathFirst = tableChatView.indexPathsForVisibleRows?.first
  2735. if indexPathFirst != nil && listViewOnSection.count != 0 && listViewOnSection.count - 1 <= indexPathFirst!.section{
  2736. let headerView = listViewOnSection[indexPathFirst!.section]
  2737. if headerView.isHidden {
  2738. headerView.isHidden = false
  2739. }
  2740. }
  2741. var listData = dataMessages[0...currentIndexpath!.row]
  2742. listData = listData.filter({$0["status"] as? String != "4" && $0["status"] as? String != "8"})
  2743. if listData.count != 0 && !isContactCenter {
  2744. let idMe = UserDefaults.standard.string(forKey: "me") as String?
  2745. for i in 0...listData.count - 1 {
  2746. if listData[i]["f_pin"] as? String != idMe {
  2747. sendReadMessageStatus(chat_id: "", f_pin: dataPerson["f_pin"]!!, message_scope_id: "3", message_id: listData[i]["message_id"] as! String)
  2748. }
  2749. }
  2750. }
  2751. }
  2752. if counter == 0 && indicatorCounterBSTB.isDescendant(of: self.view) {
  2753. indicatorCounterBSTB.removeConstraints(indicatorCounterBSTB.constraints)
  2754. indicatorCounterBSTB.removeFromSuperview()
  2755. } else if counter != 0 && currentIndexpath != nil {
  2756. let dataFilter = dataMessages.filter({ $0["chat_date"] as! String == dataDates[currentIndexpath!.section] })
  2757. if dataFilter.count == 0 {
  2758. return
  2759. }
  2760. let idx = dataMessages.firstIndex(where: { $0["message_id"] as? String == dataFilter[currentIndexpath!.row]["message_id"] as? String})
  2761. if idx == nil {
  2762. return
  2763. }
  2764. if (dataMessages.count - counter) <= idx! {
  2765. let countUpdate = idx! - (dataMessages.count - counter)
  2766. counter = counter - (countUpdate + 1)
  2767. if indicatorCounterBSTB.isDescendant(of: self.view) {
  2768. labelCounter.text = "\(counter)"
  2769. }
  2770. updateCounter(counter: counter)
  2771. }
  2772. }
  2773. }
  2774. }
  2775. //EPV
  2776. extension EditorPersonal: PreviewAttachmentImageVideoDelegate {
  2777. public func didSelect(imagevideo: Any?) {
  2778. if (imagevideo != nil) {
  2779. let imageVideoData = imagevideo as! [UIImagePickerController.InfoKey: Any]
  2780. let previewImageVC = PreviewAttachmentImageVideo(nibName: "PreviewAttachmentImageVideo", bundle: Bundle.resourceBundle(for: Nexilis.self))
  2781. previewImageVC.imageVideoData = imageVideoData
  2782. if (textFieldSend.textColor != .lightGray) {
  2783. previewImageVC.currentTextTextField = textFieldSend.text
  2784. }
  2785. previewImageVC.modalPresentationStyle = .custom
  2786. previewImageVC.delegate = self
  2787. previewImageVC.isAck = self.isAck
  2788. previewImageVC.isConfidential = self.isConfidential
  2789. previewImageVC.isCC = self.isContactCenter
  2790. self.present(previewImageVC, animated: true, completion: nil)
  2791. }
  2792. }
  2793. func sendChatFromPreviewImage(message_text: String, attachment_flag: String, image_id: String, video_id: String, thumb_id: String, viewController: UIViewController) {
  2794. sendChat(message_text: message_text, attachment_flag: attachment_flag, image_id: image_id, video_id: video_id, thumb_id: thumb_id, viewController: viewController)
  2795. }
  2796. }
  2797. //EQL
  2798. extension EditorPersonal: UIDocumentPickerDelegate, DocumentPickerDelegate, QLPreviewControllerDataSource {
  2799. public func didSelectDocument(document: Any?) {
  2800. if (document != nil) {
  2801. self.previewItem = (document as! [URL])[0] as NSURL
  2802. let previewController = QLPreviewController()
  2803. let navController = UINavigationController(rootViewController: previewController)
  2804. let leftBarButton = navigationQLPreviewDocument(title: "Cancel".localized(), style: .plain, target: self, action: #selector(cancelDocumentPreview))
  2805. let rightBarButton = navigationQLPreviewDocument(title: "Send".localized(), style: .done, target: self, action: #selector(sendDocument))
  2806. // leftBarButton.tintColor = .white
  2807. // rightBarButton.tintColor = .white
  2808. leftBarButton.navigation = navController
  2809. rightBarButton.navigation = navController
  2810. // navController.navigationBar.barTintColor = .mainColor
  2811. navController.navigationBar.isTranslucent = false
  2812. previewController.navigationItem.leftBarButtonItem = leftBarButton
  2813. previewController.navigationItem.rightBarButtonItem = rightBarButton
  2814. previewController.dataSource = self
  2815. previewController.modalPresentationStyle = .pageSheet
  2816. self.present(navController, animated: true, completion: nil)
  2817. }
  2818. }
  2819. @objc private func cancelDocumentPreview(sender: navigationQLPreviewDocument) {
  2820. sender.navigation.dismiss(animated: true, completion: nil)
  2821. }
  2822. @objc private func sendDocument(sender: navigationQLPreviewDocument) {
  2823. sender.navigation.dismiss(animated: true, completion: nil)
  2824. do {
  2825. let dataFile = try Data(contentsOf: self.previewItem! as URL)
  2826. let urlFile = self.previewItem?.absoluteString
  2827. var originaFileName = (urlFile! as NSString).lastPathComponent
  2828. originaFileName = NSString(string: originaFileName).removingPercentEncoding!
  2829. let renamedNameFile = "Qmera_doc_" + "\(Date().currentTimeMillis())_" + originaFileName
  2830. let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
  2831. let fileURL = documentsDirectory.appendingPathComponent(renamedNameFile)
  2832. if !FileManager.default.fileExists(atPath: fileURL.path) {
  2833. do {
  2834. try dataFile.write(to: fileURL)
  2835. //print("file saved")
  2836. } catch {
  2837. //print("error saving file:", error)
  2838. }
  2839. }
  2840. sendChat(message_text: "\(originaFileName)|", attachment_flag: "6", file_id: renamedNameFile, viewController: self)
  2841. } catch {
  2842. }
  2843. }
  2844. public func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
  2845. 1
  2846. }
  2847. public func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
  2848. return self.previewItem!
  2849. }
  2850. }
  2851. //ETV
  2852. extension EditorPersonal: UITextViewDelegate {
  2853. public func textViewDidChangeSelection(_ textView: UITextView) {
  2854. let cursorPosition = textView.caretRect(for: self.textFieldSend.selectedTextRange!.start).origin
  2855. let currentLine = Int(cursorPosition.y / self.textFieldSend.font!.lineHeight)
  2856. UIView.animate(withDuration: 0.3) {
  2857. let numberOfLines = textView.textContainer.lineBreakMode == .byWordWrapping ? Int(textView.contentSize.height / textView.font!.lineHeight) - 1 : 1
  2858. if currentLine == 0 && numberOfLines == 1 {
  2859. self.heightTextFieldSend.constant = 40
  2860. } else if self.heightTextFieldSend.constant < 95.0 && currentLine >= 4 {
  2861. self.heightTextFieldSend.constant = 95.0
  2862. } else if currentLine < 4 && numberOfLines < 5 {
  2863. if (self.textFieldSend.text.count > 0 && self.heightTextFieldSend.constant != self.textFieldSend.contentSize.height) {
  2864. self.heightTextFieldSend.constant = self.textFieldSend.contentSize.height
  2865. }
  2866. }
  2867. }
  2868. }
  2869. public func textViewDidChange(_ textView: UITextView) {
  2870. if textView.text.count == 0 {
  2871. isAlwaysHideLinkPreview = false
  2872. }
  2873. if allowTyping {
  2874. allowTyping = false
  2875. if isContactCenter && !fPinContacCenter.isEmpty {
  2876. sendTyping(l_pin: fPinContacCenter, isTyping: true)
  2877. } else {
  2878. sendTyping(l_pin: dataPerson["f_pin"]!!, isTyping: true)
  2879. }
  2880. DispatchQueue.main.asyncAfter(deadline: .now() + 4, execute: {
  2881. self.allowTyping = true
  2882. })
  2883. }
  2884. timerCheckLink?.invalidate()
  2885. timerCheckLink = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: {_ in
  2886. self.checkLink(fullText: textView.text)
  2887. })
  2888. if textView.text.contains("*") || textView.text.contains("_") || textView.text.contains("^") || textView.text.contains("~") {
  2889. textView.preserveCursorPosition(withChanges: { _ in
  2890. textView.attributedText = textView.text.richText(isEditing: true)
  2891. return .preserveCursor
  2892. })
  2893. }
  2894. }
  2895. private func checkLink(fullText: String) {
  2896. if !isAlwaysHideLinkPreview {
  2897. var text = ""
  2898. let listTextSplitBreak = fullText.components(separatedBy: "\n")
  2899. let indexFirstLinkSplitBreak = listTextSplitBreak.firstIndex(where: { $0.contains("www.") || $0.contains("http://") || $0.contains("https://") })
  2900. if indexFirstLinkSplitBreak != nil {
  2901. let listTextSplitSpace = listTextSplitBreak[indexFirstLinkSplitBreak!].components(separatedBy: " ")
  2902. 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) })
  2903. if indexFirstLinkSplitSpace != nil {
  2904. text = listTextSplitSpace[indexFirstLinkSplitSpace!]
  2905. }
  2906. }
  2907. if !text.isEmpty {
  2908. var stringURl = text
  2909. if stringURl.starts(with: "www.") {
  2910. stringURl = "https://" + stringURl.replacingOccurrences(of: "www.", with: "")
  2911. }
  2912. var dataURL = ""
  2913. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  2914. if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "select data_link from LINK_PREVIEW where link='\(text)'") {
  2915. while cursor.next() {
  2916. if let data = cursor.string(forColumnIndex: 0) {
  2917. dataURL = data
  2918. }
  2919. }
  2920. cursor.close()
  2921. }
  2922. })
  2923. if !dataURL.isEmpty {
  2924. if let data = try! JSONSerialization.jsonObject(with: dataURL.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
  2925. let title = data["title"] as! String
  2926. let description = data["description"] as! String
  2927. let imageUrl = data["imageUrl"] as? String
  2928. let link = data["link"] as! String
  2929. if self.showingLink != text {
  2930. self.showingLink = text
  2931. self.deleteLinkPreview()
  2932. self.buildPreviewLink(imageUrl: imageUrl, title: title, description: description, stringURl: link)
  2933. }
  2934. }
  2935. } else {
  2936. let slp = SwiftLinkPreview(session: URLSession.shared,
  2937. workQueue: SwiftLinkPreview.defaultWorkQueue,
  2938. responseQueue: DispatchQueue.main,
  2939. cache: DisabledCache.instance)
  2940. let preview = slp.preview(stringURl,
  2941. onSuccess: { result in
  2942. let title = result.title ?? "No Title"
  2943. let description = stringURl.contains("google.com") ? "" : result.description
  2944. let imageUrl = result.image
  2945. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  2946. do {
  2947. var dataJson: [String: Any] = [:]
  2948. dataJson["title"] = title
  2949. dataJson["description"] = description
  2950. dataJson["imageUrl"] = imageUrl
  2951. dataJson["link"] = text
  2952. guard let json = String(data: try! JSONSerialization.data(withJSONObject: dataJson, options: []), encoding: String.Encoding.utf8) else {
  2953. return
  2954. }
  2955. _ = try Database.shared.insertRecord(fmdb: fmdb, table: "LINK_PREVIEW", cvalues: [
  2956. "id" : "\(Date().currentTimeMillis().toHex())",
  2957. "link" : text,
  2958. "data_link" : json,
  2959. "retry": 0
  2960. ], replace: true)
  2961. } catch {
  2962. rollback.pointee = true
  2963. //print(error)
  2964. }
  2965. })
  2966. if self.showingLink != text {
  2967. self.showingLink = text
  2968. self.deleteLinkPreview()
  2969. self.buildPreviewLink(imageUrl: imageUrl, title: title, description: description, stringURl: text)
  2970. }
  2971. },
  2972. onError: { error in
  2973. self.deleteLinkPreview()
  2974. })
  2975. }
  2976. } else {
  2977. deleteLinkPreview()
  2978. }
  2979. }
  2980. }
  2981. private func buildPreviewLink(imageUrl: String?, title: String, description: String?, stringURl: String) {
  2982. if !self.viewTextfield.subviews.contains(self.containerLink){
  2983. UIView.animate(withDuration: 0.25, delay: 0.0, options: .curveEaseInOut, animations: {
  2984. self.constraintTopTextField.constant = self.constraintTopTextField.constant + 80
  2985. }, completion: nil)
  2986. }
  2987. self.viewTextfield.addSubview(self.containerLink)
  2988. self.containerLink.translatesAutoresizingMaskIntoConstraints = false
  2989. self.containerLink.leadingAnchor.constraint(equalTo: self.viewTextfield.leadingAnchor).isActive = true
  2990. self.containerLink.bottomAnchor.constraint(equalTo: self.textFieldSend.topAnchor).isActive = true
  2991. self.containerLink.trailingAnchor.constraint(equalTo: self.viewTextfield.trailingAnchor).isActive = true
  2992. self.containerLink.heightAnchor.constraint(equalToConstant: 80.0).isActive = true
  2993. self.containerLink.backgroundColor = .secondaryColor
  2994. if self.reffId != nil {
  2995. self.bottomAnchorPreviewReply.isActive = false
  2996. self.bottomAnchorPreviewReply = self.containerPreviewReply.bottomAnchor.constraint(equalTo: self.containerLink.topAnchor)
  2997. self.bottomAnchorPreviewReply.isActive = true
  2998. }
  2999. let imagePreview = UIImageView()
  3000. if imageUrl != nil {
  3001. self.containerLink.addSubview(imagePreview)
  3002. imagePreview.translatesAutoresizingMaskIntoConstraints = false
  3003. imagePreview.leadingAnchor.constraint(equalTo: self.containerLink.leadingAnchor).isActive = true
  3004. imagePreview.bottomAnchor.constraint(equalTo: self.containerLink.bottomAnchor).isActive = true
  3005. imagePreview.topAnchor.constraint(equalTo: self.containerLink.topAnchor).isActive = true
  3006. imagePreview.widthAnchor.constraint(equalToConstant: 80.0).isActive = true
  3007. if !imageUrl!.starts(with: "https://") {
  3008. imagePreview.loadImageAsync(with: "https://www.google.be" + imageUrl!)
  3009. } else {
  3010. imagePreview.loadImageAsync(with: imageUrl)
  3011. }
  3012. imagePreview.contentMode = .scaleAspectFit
  3013. }
  3014. let titlePreview = UILabel()
  3015. self.containerLink.addSubview(titlePreview)
  3016. titlePreview.translatesAutoresizingMaskIntoConstraints = false
  3017. if imageUrl != nil {
  3018. titlePreview.leadingAnchor.constraint(equalTo: imagePreview.trailingAnchor, constant: 5.0).isActive = true
  3019. } else {
  3020. titlePreview.leadingAnchor.constraint(equalTo: self.containerLink.leadingAnchor, constant: 5.0).isActive = true
  3021. }
  3022. titlePreview.topAnchor.constraint(equalTo: self.containerLink.topAnchor, constant: 25.0).isActive = true
  3023. titlePreview.trailingAnchor.constraint(equalTo: self.containerLink.trailingAnchor, constant: -80.0).isActive = true
  3024. titlePreview.text = title
  3025. titlePreview.font = UIFont.systemFont(ofSize: 14.0, weight: .bold)
  3026. titlePreview.textColor = .black
  3027. let descPreview = UILabel()
  3028. self.containerLink.addSubview(descPreview)
  3029. descPreview.translatesAutoresizingMaskIntoConstraints = false
  3030. if imageUrl != nil {
  3031. descPreview.leadingAnchor.constraint(equalTo: imagePreview.trailingAnchor, constant: 5.0).isActive = true
  3032. } else {
  3033. descPreview.leadingAnchor.constraint(equalTo: self.containerLink.leadingAnchor, constant: 5.0).isActive = true
  3034. }
  3035. descPreview.topAnchor.constraint(equalTo: titlePreview.bottomAnchor).isActive = true
  3036. descPreview.trailingAnchor.constraint(equalTo: self.containerLink.trailingAnchor, constant: -80.0).isActive = true
  3037. descPreview.text = description
  3038. descPreview.font = UIFont.systemFont(ofSize: 12.0)
  3039. descPreview.textColor = .gray
  3040. descPreview.numberOfLines = 1
  3041. let linkPreview = UILabel()
  3042. self.containerLink.addSubview(linkPreview)
  3043. linkPreview.translatesAutoresizingMaskIntoConstraints = false
  3044. if imageUrl != nil {
  3045. linkPreview.leadingAnchor.constraint(equalTo: imagePreview.trailingAnchor, constant: 5.0).isActive = true
  3046. } else {
  3047. linkPreview.leadingAnchor.constraint(equalTo: self.containerLink.leadingAnchor, constant: 5.0).isActive = true
  3048. }
  3049. linkPreview.topAnchor.constraint(equalTo: descPreview.bottomAnchor, constant: 8.0).isActive = true
  3050. linkPreview.trailingAnchor.constraint(equalTo: self.containerLink.trailingAnchor, constant: -80.0).isActive = true
  3051. linkPreview.text = stringURl
  3052. linkPreview.font = UIFont.systemFont(ofSize: 10.0)
  3053. linkPreview.textColor = .gray
  3054. linkPreview.numberOfLines = 1
  3055. let cancelPreview = UIButton(type: .custom)
  3056. self.containerLink.addSubview(cancelPreview)
  3057. cancelPreview.translatesAutoresizingMaskIntoConstraints = false
  3058. cancelPreview.trailingAnchor.constraint(equalTo: self.containerLink.trailingAnchor, constant: -10).isActive = true
  3059. cancelPreview.centerYAnchor.constraint(equalTo: self.containerLink.centerYAnchor).isActive = true
  3060. cancelPreview.setImage(UIImage(systemName: "xmark.circle" , withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .regular, scale: .default)), for: .normal)
  3061. cancelPreview.addTarget(nil, action: #selector(self.removeLinkPreviewUntilEmptyTextView), for: .touchUpInside)
  3062. cancelPreview.backgroundColor = .clear
  3063. cancelPreview.tintColor = .mainColor
  3064. }
  3065. public func textViewDidBeginEditing(_ textView: UITextView) {
  3066. if textView.textColor == UIColor.lightGray {
  3067. textView.text = nil
  3068. textView.textColor = UIColor.black
  3069. }
  3070. }
  3071. public func textViewDidEndEditing(_ textView: UITextView) {
  3072. if textView.text.isEmpty {
  3073. textView.text = "Send message".localized()
  3074. textView.textColor = UIColor.lightGray
  3075. }
  3076. }
  3077. public override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
  3078. if action == #selector(UIResponderStandardEditActions.paste(_:)) && (UIPasteboard.general.image != nil) {
  3079. return true
  3080. }
  3081. return super.canPerformAction(action, withSender: sender)
  3082. }
  3083. public override func paste(_ sender: Any?) {
  3084. if UIPasteboard.general.image != nil {
  3085. let previewImageVC = PreviewAttachmentImageVideo(nibName: "PreviewAttachmentImageVideo", bundle: Bundle.resourceBundle(for: Nexilis.self))
  3086. previewImageVC.image = UIPasteboard.general.image
  3087. previewImageVC.fromCopy = true
  3088. previewImageVC.currentTextTextField = textFieldSend.text
  3089. previewImageVC.modalPresentationStyle = .custom
  3090. previewImageVC.delegate = self
  3091. previewImageVC.isAck = self.isAck
  3092. previewImageVC.isConfidential = self.isConfidential
  3093. previewImageVC.isCC = self.isContactCenter
  3094. self.present(previewImageVC, animated: true, completion: nil)
  3095. }
  3096. }
  3097. public func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
  3098. if (self.textFieldSend.text.count == 0) {
  3099. return text != "\n"
  3100. }
  3101. return true
  3102. }
  3103. }
  3104. //EUC
  3105. extension EditorPersonal: UIContextMenuInteractionDelegate {
  3106. public func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willEndFor configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionAnimating?) {
  3107. if isLinkCopied {
  3108. isLinkCopied = false
  3109. interaction.view!.removeInteraction(interaction)
  3110. }
  3111. }
  3112. public func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
  3113. if textFieldSend.isFirstResponder {
  3114. textFieldSend.resignFirstResponder()
  3115. }
  3116. let indexPath = self.tableChatView.indexPathForRow(at: interaction.view!.convert(location, to: self.tableChatView))
  3117. let dataMessages = self.dataMessages.filter({ $0["chat_date"] as! String == dataDates[indexPath!.section]})
  3118. var star: UIAction
  3119. if (dataMessages[indexPath!.row]["is_stared"] as! String == "0") {
  3120. star = UIAction(title: "Star".localized(), image: UIImage(systemName: "star.fill"), handler: {(_) in
  3121. if self.removed {
  3122. return
  3123. }
  3124. DispatchQueue.global().async {
  3125. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  3126. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  3127. "is_stared" : 1
  3128. ], _where: "message_id = '\(dataMessages[indexPath!.row]["message_id"] as! String)'")
  3129. })
  3130. }
  3131. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[indexPath!.row]["message_id"] as? String})
  3132. if idx != nil{
  3133. self.dataMessages[idx!]["is_stared"] = "1"
  3134. }
  3135. self.tableChatView.reloadRows(at: [indexPath!], with: .none)
  3136. })
  3137. } else {
  3138. star = UIAction(title: "Unstar".localized(), image: UIImage(systemName: "star.fill"), handler: {(_) in
  3139. if self.removed {
  3140. return
  3141. }
  3142. DispatchQueue.global().async {
  3143. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  3144. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  3145. "is_stared" : 0
  3146. ], _where: "message_id = '\(dataMessages[indexPath!.row]["message_id"] as! String)'")
  3147. })
  3148. }
  3149. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[indexPath!.row]["message_id"] as? String})
  3150. if idx != nil{
  3151. self.dataMessages[idx!]["is_stared"] = "0"
  3152. }
  3153. self.tableChatView.reloadRows(at: [indexPath!], with: .none)
  3154. })
  3155. }
  3156. let reply = UIAction(title: "Reply".localized(), image: UIImage(systemName: "arrowshape.turn.up.left.fill"), handler: {(_) in
  3157. if self.removed {
  3158. return
  3159. }
  3160. if self.isSearching {
  3161. self.cancelAction()
  3162. }
  3163. DispatchQueue.main.asyncAfter(deadline: .now() + 0.35, execute: {
  3164. self.handleReply(indexPath: indexPath!)
  3165. })
  3166. })
  3167. let forward = UIAction(title: "Forward".localized(), image: UIImage(systemName: "arrowshape.turn.up.right.fill"), handler: {(_) in
  3168. if self.removed {
  3169. return
  3170. }
  3171. if self.isSearching {
  3172. self.cancelAction()
  3173. }
  3174. if self.reffId != nil {
  3175. self.deleteReplyView()
  3176. }
  3177. DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
  3178. self.forwardSession = true
  3179. let cancelButton = UIBarButtonItem(title: "Cancel".localized(), style: .plain, target: self, action: #selector(self.cancelAction))
  3180. if self.dataPerson["f_pin"] != "-999" && !self.isContactCenter {
  3181. self.navigationItem.rightBarButtonItems = nil
  3182. }
  3183. self.navigationItem.rightBarButtonItem = cancelButton
  3184. if self.isContactCenter || self.fromNotification {
  3185. self.navigationItem.leftBarButtonItem = nil
  3186. }
  3187. self.changeAppBar()
  3188. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[indexPath!.row]["message_id"] as? String})
  3189. if idx != nil{
  3190. self.dataMessages[idx!]["isSelected"] = true
  3191. }
  3192. self.addMultipleSelectSession()
  3193. self.tableChatView.reloadData()
  3194. }
  3195. })
  3196. let copy = UIAction(title: "Copy".localized(), image: UIImage(systemName: "doc.on.doc.fill"), handler: {(_) in
  3197. if self.removed {
  3198. return
  3199. }
  3200. if self.isSearching {
  3201. self.cancelAction()
  3202. }
  3203. if self.reffId != nil {
  3204. self.deleteReplyView()
  3205. }
  3206. DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
  3207. self.copySession = true
  3208. let cancelButton = UIBarButtonItem(title: "Cancel".localized(), style: .plain, target: self, action: #selector(self.cancelAction))
  3209. if self.dataPerson["f_pin"] != "-999" && !self.isContactCenter {
  3210. self.navigationItem.rightBarButtonItems = nil
  3211. }
  3212. self.navigationItem.rightBarButtonItem = cancelButton
  3213. if self.isContactCenter || self.fromNotification {
  3214. self.navigationItem.leftBarButtonItem = nil
  3215. }
  3216. self.changeAppBar()
  3217. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[indexPath!.row]["message_id"] as? String})
  3218. if idx != nil{
  3219. self.dataMessages[idx!]["isSelected"] = true
  3220. }
  3221. self.addMultipleSelectSession()
  3222. self.tableChatView.reloadData()
  3223. }
  3224. })
  3225. let info = UIAction(title: "Info".localized(), image: UIImage(systemName: "info.circle.fill"), handler: {(_) in
  3226. if self.removed {
  3227. return
  3228. }
  3229. let messageInfoVC = MessageInfo()
  3230. messageInfoVC.data = dataMessages[indexPath!.row]
  3231. messageInfoVC.dataPerson = self.dataPerson
  3232. self.navigationController?.pushViewController(messageInfoVC, animated: true)
  3233. })
  3234. let delete = UIAction(title: "Delete".localized(), image: UIImage(systemName: "trash.fill"), attributes: .destructive, handler: {(_) in
  3235. if self.removed {
  3236. return
  3237. }
  3238. if self.isSearching {
  3239. self.cancelAction()
  3240. }
  3241. if self.reffId != nil {
  3242. self.deleteReplyView()
  3243. }
  3244. DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
  3245. self.deleteSession = true
  3246. let cancelButton = UIBarButtonItem(title: "Cancel".localized(), style: .plain, target: self, action: #selector(self.cancelAction))
  3247. if self.dataPerson["f_pin"] != "-999" && !self.isContactCenter {
  3248. self.navigationItem.rightBarButtonItems = nil
  3249. }
  3250. self.navigationItem.rightBarButtonItem = cancelButton
  3251. if self.isContactCenter || self.fromNotification {
  3252. self.navigationItem.leftBarButtonItem = nil
  3253. }
  3254. self.changeAppBar()
  3255. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[indexPath!.row]["message_id"] as? String})
  3256. if idx != nil{
  3257. self.dataMessages[idx!]["isSelected"] = true
  3258. }
  3259. self.addMultipleSelectSession()
  3260. self.tableChatView.reloadData()
  3261. }
  3262. })
  3263. var children: [UIMenuElement] = [star, reply, forward, copy, delete]
  3264. // let copyOption = self.copyOption(indexPath: indexPath!)
  3265. let idMe = UserDefaults.standard.string(forKey: "me") as String?
  3266. if isContactCenter {
  3267. children = [reply, copy]
  3268. } 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" {
  3269. children = [delete]
  3270. } else if blocking == "1" || blocking == "-1" {
  3271. children = [star, forward, copy ,delete]
  3272. if !(dataMessages[indexPath!.row]["image_id"] as! String).isEmpty || !(dataMessages[indexPath!.row]["video_id"] as! String).isEmpty {
  3273. children = [star, forward ,delete]
  3274. }
  3275. if (dataMessages[indexPath!.row]["f_pin"] as! String) == idMe {
  3276. children.insert(info, at: children.count - 1)
  3277. }
  3278. }
  3279. 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 || dataMessages[indexPath!.row]["attachment_flag"] as! String == "11" {
  3280. children = [star, reply, forward ,delete]
  3281. if (dataMessages[indexPath!.row]["f_pin"] as! String) == idMe {
  3282. children.insert(info, at: children.count - 1)
  3283. }
  3284. } else {
  3285. if (dataMessages[indexPath!.row]["f_pin"] as! String) == idMe {
  3286. children.insert(info, at: children.count - 1)
  3287. }
  3288. }
  3289. return UIContextMenuConfiguration(identifier: nil,
  3290. previewProvider: nil) { _ in
  3291. UIMenu(title: "", children: children)
  3292. }
  3293. }
  3294. @objc func cancelAction() {
  3295. DispatchQueue.main.async {
  3296. if self.copySession {
  3297. self.copySession = false
  3298. } else if self.forwardSession {
  3299. self.forwardSession = false
  3300. } else if self.deleteSession {
  3301. self.deleteSession = false
  3302. } else if self.isSearching {
  3303. self.countMatchesSearch = 0
  3304. self.isSearching = false
  3305. }
  3306. if self.viewTextfield.isHidden {
  3307. self.viewTextfield.isHidden = false
  3308. }
  3309. if self.viewAttachment.isHidden {
  3310. self.viewAttachment.isHidden = false
  3311. }
  3312. if self.containerAction.isHidden {
  3313. self.containerAction.isHidden = false
  3314. }
  3315. if self.constraintBottomTableViewWithTextfield.constant == -60.0 {
  3316. self.constraintBottomTableViewWithTextfield.constant = self.constraintBottomTableViewWithTextfield.constant + 70
  3317. DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: {
  3318. if (self.currentIndexpath != nil) {
  3319. self.tableChatView.scrollToRow(at: IndexPath(row: self.currentIndexpath!.row, section: self.currentIndexpath!.section), at: .none, animated: true)
  3320. } else {
  3321. self.tableChatView.scrollToBottom()
  3322. }
  3323. })
  3324. }
  3325. let data = self.dataMessages.filter({ $0["isSelected"] as! Bool == true })
  3326. for i in 0..<data.count {
  3327. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == data[i]["message_id"] as? String})
  3328. if idx != nil{
  3329. self.dataMessages[idx!]["isSelected"] = false
  3330. }
  3331. }
  3332. self.tableChatView.reloadData()
  3333. self.setRightButtonItem()
  3334. self.changeAppBar()
  3335. if self.isContactCenter || self.fromNotification {
  3336. let backButton = UIBarButtonItem(image: UIImage(systemName: "chevron.backward"), style: .plain, target: self, action: #selector(self.didTapExit))
  3337. self.navigationItem.leftBarButtonItem = backButton
  3338. }
  3339. self.containerMultpileSelectSession.removeFromSuperview()
  3340. self.checkNewMessage(tableView: self.tableChatView)
  3341. }
  3342. }
  3343. private func addMultipleSelectSession() {
  3344. viewTextfield.isHidden = true
  3345. viewAttachment.isHidden = true
  3346. containerAction.isHidden = true
  3347. constraintBottomTableViewWithTextfield.constant = constraintBottomTableViewWithTextfield.constant - 70
  3348. view.addSubview(containerMultpileSelectSession)
  3349. containerMultpileSelectSession.translatesAutoresizingMaskIntoConstraints = false
  3350. constraintBottomContainerMultpileSelectSession = containerMultpileSelectSession.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0)
  3351. NSLayoutConstraint.activate([
  3352. containerMultpileSelectSession.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
  3353. containerMultpileSelectSession.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
  3354. constraintBottomContainerMultpileSelectSession,
  3355. containerMultpileSelectSession.heightAnchor.constraint(equalToConstant: 50)
  3356. ])
  3357. containerMultpileSelectSession.backgroundColor = .white
  3358. addSubviewMultipleSession()
  3359. }
  3360. private func addSubviewMultipleSession() {
  3361. let container = UIView()
  3362. containerMultpileSelectSession.addSubview(container)
  3363. container.translatesAutoresizingMaskIntoConstraints = false
  3364. NSLayoutConstraint.activate([
  3365. container.leadingAnchor.constraint(equalTo: containerMultpileSelectSession.leadingAnchor),
  3366. container.trailingAnchor.constraint(equalTo:containerMultpileSelectSession.trailingAnchor),
  3367. container.bottomAnchor.constraint(equalTo: containerMultpileSelectSession.bottomAnchor),
  3368. container.heightAnchor.constraint(equalToConstant: 50)
  3369. ])
  3370. container.layer.shadowOpacity = 0.7
  3371. container.layer.shadowOffset = CGSize(width: 3, height: 3)
  3372. container.layer.shadowRadius = 3.0
  3373. container.layer.shadowColor = UIColor.black.cgColor
  3374. container.backgroundColor = .secondaryColor
  3375. if !isSearching {
  3376. let title = UILabel()
  3377. container.addSubview(title)
  3378. title.translatesAutoresizingMaskIntoConstraints = false
  3379. NSLayoutConstraint.activate([
  3380. title.centerXAnchor.constraint(equalTo: container.centerXAnchor),
  3381. title.centerYAnchor.constraint(equalTo:container.centerYAnchor),
  3382. ])
  3383. let countSelected = dataMessages.filter({ $0["isSelected"] as! Bool == true }).count
  3384. title.text = "\(countSelected) " + "Selected".localized()
  3385. title.textColor = .mainColor
  3386. title.font = UIFont.systemFont(ofSize: 15.0).bold
  3387. let button = UIImageView()
  3388. container.addSubview(button)
  3389. button.translatesAutoresizingMaskIntoConstraints = false
  3390. NSLayoutConstraint.activate([
  3391. button.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 15),
  3392. button.centerYAnchor.constraint(equalTo:container.centerYAnchor),
  3393. button.widthAnchor.constraint(equalToConstant: 30),
  3394. button.heightAnchor.constraint(equalToConstant: 30),
  3395. ])
  3396. if copySession {
  3397. button.image = UIImage(systemName: "doc.on.doc")
  3398. if countSelected == 0 {
  3399. button.tintColor = .gray
  3400. } else {
  3401. button.tintColor = .mainColor
  3402. }
  3403. } else if forwardSession {
  3404. button.image = UIImage(systemName: "arrowshape.turn.up.right")
  3405. if countSelected == 0 {
  3406. button.tintColor = .gray
  3407. } else {
  3408. button.tintColor = .mainColor
  3409. }
  3410. } else if deleteSession {
  3411. button.image = UIImage(systemName: "trash")
  3412. if countSelected == 0 {
  3413. button.tintColor = .gray
  3414. } else {
  3415. button.tintColor = .red
  3416. }
  3417. }
  3418. let buttonGesture = UITapGestureRecognizer(target: self, action: #selector(sessionAction))
  3419. button.isUserInteractionEnabled = true
  3420. button.addGestureRecognizer(buttonGesture)
  3421. } else {
  3422. buttonUp = UIButton()
  3423. container.addSubview(buttonUp)
  3424. buttonUp.translatesAutoresizingMaskIntoConstraints = false
  3425. NSLayoutConstraint.activate([
  3426. buttonUp.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 10),
  3427. buttonUp.centerYAnchor.constraint(equalTo:container.centerYAnchor),
  3428. buttonUp.widthAnchor.constraint(equalToConstant: 30),
  3429. buttonUp.heightAnchor.constraint(equalToConstant: 30),
  3430. ])
  3431. buttonUp.addTarget(self, action: #selector(upSearchText), for: .touchUpInside)
  3432. buttonDown = UIButton()
  3433. container.addSubview(buttonDown)
  3434. buttonDown.translatesAutoresizingMaskIntoConstraints = false
  3435. NSLayoutConstraint.activate([
  3436. buttonDown.leadingAnchor.constraint(equalTo: buttonUp.trailingAnchor, constant: 15),
  3437. buttonDown.centerYAnchor.constraint(equalTo:container.centerYAnchor),
  3438. buttonDown.widthAnchor.constraint(equalToConstant: 30),
  3439. buttonDown.heightAnchor.constraint(equalToConstant: 30),
  3440. ])
  3441. buttonDown.addTarget(self, action: #selector(downSearchText), for: .touchUpInside)
  3442. buttonUp.setImage(UIImage(systemName: "chevron.up"), for: .normal)
  3443. buttonUp.tintColor = .gray
  3444. buttonDown.setImage(UIImage(systemName: "chevron.down"), for: .normal)
  3445. buttonDown.tintColor = .gray
  3446. titleSearchMatches = UILabel()
  3447. container.addSubview(titleSearchMatches)
  3448. titleSearchMatches.translatesAutoresizingMaskIntoConstraints = false
  3449. NSLayoutConstraint.activate([
  3450. titleSearchMatches.centerXAnchor.constraint(equalTo: container.centerXAnchor),
  3451. titleSearchMatches.centerYAnchor.constraint(equalTo:container.centerYAnchor),
  3452. ])
  3453. titleSearchMatches.textColor = .mainColor
  3454. titleSearchMatches.font = UIFont.systemFont(ofSize: 15.0).bold
  3455. titleSearchMatches.isHidden = true
  3456. DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: {
  3457. self.searchBar.becomeFirstResponder()
  3458. })
  3459. }
  3460. }
  3461. @objc func upSearchText() {
  3462. scrollToFirstSearchMessage(indexScroll: lastScrollIdxSearch + 1)
  3463. }
  3464. @objc func downSearchText() {
  3465. scrollToFirstSearchMessage(indexScroll: lastScrollIdxSearch - 1)
  3466. }
  3467. @objc func sessionAction() {
  3468. if copySession {
  3469. let dataMessages = self.dataMessages.filter({ $0["isSelected"] as! Bool == true })
  3470. let countSelected = dataMessages.count
  3471. if countSelected == 0 {
  3472. return
  3473. }
  3474. var text = ""
  3475. for i in 0..<countSelected {
  3476. let stringDate = (dataMessages[i]["server_date"] as! String)
  3477. let date = Date(milliseconds: Int64(stringDate)!)
  3478. let formatterDate = DateFormatter()
  3479. let formatterTime = DateFormatter()
  3480. formatterDate.dateFormat = "dd/MM/yy"
  3481. formatterDate.locale = NSLocale(localeIdentifier: "id") as Locale?
  3482. formatterTime.dateFormat = "HH:mm"
  3483. formatterTime.locale = NSLocale(localeIdentifier: "id") as Locale?
  3484. let dataProfile = getDataProfile(message_id: dataMessages[i]["message_id"] as! String)
  3485. if text.isEmpty {
  3486. text = "*[\(formatterDate.string(from: date as Date)) \(formatterTime.string(from: date as Date))] \(dataProfile["name"]!):*\n\(dataMessages[i]["message_text"] as! String)"
  3487. } else {
  3488. 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)"
  3489. }
  3490. }
  3491. text = text + "\n\n\nchat " + "Powered by Nexilis".localized()
  3492. DispatchQueue.main.async {
  3493. UIPasteboard.general.string = text
  3494. self.showToast(message: "Text coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
  3495. }
  3496. cancelAction()
  3497. } else if forwardSession {
  3498. let dataMessages = self.dataMessages.filter({ $0["isSelected"] as! Bool == true })
  3499. let countSelected = dataMessages.count
  3500. if countSelected == 0 {
  3501. return
  3502. }
  3503. contactChatNav.modalPresentationStyle = .custom
  3504. contactChatNav.navigationBar.tintColor = .white
  3505. contactChatNav.navigationBar.barTintColor = .mainColor
  3506. contactChatNav.navigationBar.isTranslucent = false
  3507. let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
  3508. contactChatNav.navigationBar.titleTextAttributes = textAttributes
  3509. contactChatNav.view.backgroundColor = .mainColor
  3510. if let controller = contactChatNav.viewControllers.first as? ContactChatViewController {
  3511. controller.isChooser = { [weak self] scope, pin in
  3512. if scope == "3" {
  3513. let editorPersonalVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorPersonalVC") as! EditorPersonal
  3514. editorPersonalVC.unique_l_pin = pin
  3515. editorPersonalVC.dataMessageForward = dataMessages
  3516. self?.navigationController?.replaceAllViewController(with: editorPersonalVC, animated: true)
  3517. } else {
  3518. let editorGroupVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorGroupVC") as! EditorGroup
  3519. editorGroupVC.unique_l_pin = pin
  3520. editorGroupVC.dataMessageForward = dataMessages
  3521. self?.navigationController?.replaceAllViewController(with: editorGroupVC, animated: true)
  3522. }
  3523. }
  3524. }
  3525. self.present(contactChatNav, animated: true, completion: nil)
  3526. } else if deleteSession {
  3527. let dataMessages = self.dataMessages.filter({ $0["isSelected"] as! Bool == true })
  3528. let countSelected = dataMessages.count
  3529. if countSelected == 0 {
  3530. return
  3531. }
  3532. let alertController = LibAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
  3533. if let action = self.actionDelete(for: "me", title: "Delete".localized() + " \(countSelected) " + "For Me".localized(), dataMessages: dataMessages) {
  3534. alertController.addAction(action)
  3535. }
  3536. let idMe = UserDefaults.standard.string(forKey: "me") as String?
  3537. let dataFilterFpin = dataMessages.filter({ $0["l_pin"] as? String == idMe})
  3538. let dataFilterLock = dataMessages.filter({ $0["lock"] as? String == "1" || $0["lock"] as? String == "2" })
  3539. if dataFilterFpin.count == 0 && dataFilterLock.count == 0 {
  3540. if let action = self.actionDelete(for: "everyone", title: "Delete".localized() + " \(countSelected) " + "For Everyone".localized(), dataMessages: dataMessages) {
  3541. alertController.addAction(action)
  3542. }
  3543. }
  3544. alertController.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: nil))
  3545. self.present(alertController, animated: true)
  3546. }
  3547. }
  3548. private func getDataProfile(message_id: String) -> [String: String]{
  3549. var data: [String: String] = [:]
  3550. Database().database?.inTransaction({ fmdb, rollback in
  3551. if let c = Database().getRecords(fmdb: fmdb, query: "select f_display_name from MESSAGE where message_id = '\(message_id)'"), c.next() {
  3552. data["name"] = c.string(forColumnIndex: 0)!
  3553. c.close()
  3554. } else {
  3555. data["name"] = "Unknown".localized()
  3556. data["image_id"] = ""
  3557. }
  3558. })
  3559. return data
  3560. }
  3561. private func deleteMessage(l_pin: String, message_id: String, scope: String, type: String, chat: String) {
  3562. let tmessage = CoreMessage_TMessageBank.deleteMessage(l_pin: l_pin, messageId: message_id, scope: scope, type: type, chat: chat)
  3563. Nexilis.deleteQueueMessage(message: tmessage)
  3564. }
  3565. private func queryMessageReply(message_id: String) -> [String: Any?] {
  3566. var dataQuery: [String: Any] = [:]
  3567. Database().database?.inTransaction({ fmdb, rollback in
  3568. 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() {
  3569. dataQuery["message_id"] = c.string(forColumnIndex: 0)
  3570. dataQuery["f_pin"] = c.string(forColumnIndex: 1)
  3571. dataQuery["message_text"] = c.string(forColumnIndex: 2)
  3572. dataQuery["attachment_flag"] = c.string(forColumnIndex: 3)
  3573. dataQuery["thumb_id"] = c.string(forColumnIndex: 4)
  3574. dataQuery["image_id"] = c.string(forColumnIndex: 5)
  3575. dataQuery["video_id"] = c.string(forColumnIndex: 6)
  3576. dataQuery["file_id"] = c.string(forColumnIndex: 7)
  3577. c.close()
  3578. }
  3579. })
  3580. return dataQuery
  3581. }
  3582. @objc func segmentedControlValueChanged(_ sender: segmentedControllerObject) {
  3583. switch sender.selectedSegmentIndex {
  3584. case 0:
  3585. sender.navigation.viewControllers[0].children[1].view.isHidden = true
  3586. break;
  3587. case 1:
  3588. sender.navigation.viewControllers[0].children[1].view.isHidden = false
  3589. break;
  3590. default:
  3591. break;
  3592. }
  3593. }
  3594. private func copyOption(indexPath: IndexPath) -> UIMenu {
  3595. var ratingButtonTitles = ["Text".localized(), "Image".localized()]
  3596. if (dataMessages[indexPath.row]["message_text"] as! String).isEmpty {
  3597. ratingButtonTitles = ["Image".localized()]
  3598. }
  3599. let dataMessages = self.dataMessages.filter({ $0["chat_date"] as! String == dataDates[indexPath.section]})
  3600. let copyActions = ratingButtonTitles
  3601. .enumerated()
  3602. .map { index, title in
  3603. return UIAction(
  3604. title: title,
  3605. identifier: nil,
  3606. handler: {(_) in
  3607. if (dataMessages[indexPath.row]["message_text"] as! String).isEmpty {
  3608. DispatchQueue.main.async {
  3609. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  3610. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  3611. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  3612. if let dirPath = paths.first {
  3613. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(dataMessages[indexPath.row]["image_id"] as! String)
  3614. if FileManager.default.fileExists(atPath: imageURL.path) {
  3615. let image = UIImage(contentsOfFile: imageURL.path)
  3616. UIPasteboard.general.image = image
  3617. self.showToast(message: "Image coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
  3618. }
  3619. }
  3620. }
  3621. return
  3622. }
  3623. if (index == 0) {
  3624. DispatchQueue.main.async {
  3625. UIPasteboard.general.string = dataMessages[indexPath.row]["message_text"] as? String
  3626. self.showToast(message: "Text coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
  3627. }
  3628. } else {
  3629. DispatchQueue.main.async {
  3630. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  3631. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  3632. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  3633. if let dirPath = paths.first {
  3634. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(dataMessages[indexPath.row]["image_id"] as! String)
  3635. if FileManager.default.fileExists(atPath: imageURL.path) {
  3636. let image = UIImage(contentsOfFile: imageURL.path)
  3637. UIPasteboard.general.image = image
  3638. self.showToast(message: "Image coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
  3639. }
  3640. }
  3641. }
  3642. }
  3643. self.dismissKeyboard()
  3644. })
  3645. }
  3646. return UIMenu(
  3647. title: "Copy".localized(),
  3648. image: UIImage(systemName: "doc.on.doc.fill"),
  3649. children: copyActions)
  3650. }
  3651. private func actionDelete(for type: String, title: String, dataMessages: [[String: Any?]]) -> UIAlertAction? {
  3652. return UIAlertAction(title: title, style: .destructive) { [unowned self] _ in
  3653. for i in 0..<dataMessages.count {
  3654. if (type == "me") {
  3655. self.deleteMessage(l_pin: dataMessages[i]["l_pin"] as! String, message_id: dataMessages[i]["message_id"] as! String, scope: "3", type: "1", chat: "")
  3656. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[i]["message_id"] as? String})
  3657. if idx != nil {
  3658. self.dataMessages.remove(at: idx!)
  3659. if (idx == self.dataMessages.count - 1) {
  3660. NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
  3661. }
  3662. for i in 0..<dataDates.count {
  3663. if self.dataMessages.filter({ $0["chat_date"] as! String == dataDates[i] }).count == 0 {
  3664. dataDates.remove(at: i)
  3665. }
  3666. }
  3667. }
  3668. } else {
  3669. self.deleteMessage(l_pin: dataMessages[i]["l_pin"] as! String, message_id: dataMessages[i]["message_id"] as! String, scope: "3", type: "2", chat: "")
  3670. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[i]["message_id"] as? String})
  3671. if idx != nil {
  3672. self.dataMessages[idx!]["lock"] = "1"
  3673. self.dataMessages[idx!]["attachment_flag"] = "0"
  3674. self.dataMessages[idx!]["reff_id"] = ""
  3675. }
  3676. }
  3677. if self.listTimerCredential[dataMessages[i]["message_id"] as! String] != nil {
  3678. self.listTimerCredential.removeValue(forKey: dataMessages[i]["message_id"] as! String)
  3679. self.timerCredential[dataMessages[i]["message_id"] as! String]?.invalidate()
  3680. self.timerCredential.removeValue(forKey: dataMessages[i]["message_id"] as! String)
  3681. }
  3682. }
  3683. cancelAction()
  3684. }
  3685. }
  3686. private func updateProfile() {
  3687. let idMe = UserDefaults.standard.string(forKey: "me") as String?
  3688. DispatchQueue.global().async {
  3689. let message = CoreMessage_TMessageBank.getBatchBuddiesInfos(p_f_pin: idMe!, last_update: 0)
  3690. let _ = Nexilis.write(message: message)
  3691. }
  3692. }
  3693. private func generateQRCode(from string: String) -> UIImage? {
  3694. let data = string.data(using: String.Encoding.ascii)
  3695. if let filter = CIFilter(name: "CIQRCodeGenerator") {
  3696. filter.setValue(data, forKey: "inputMessage")
  3697. let transform = CGAffineTransform(scaleX: 3, y: 3)
  3698. if let output = filter.outputImage?.transformed(by: transform) {
  3699. return UIImage(ciImage: output)
  3700. }
  3701. }
  3702. return nil
  3703. }
  3704. @objc func deleteReplyView() {
  3705. if self.containerPreviewReply.isDescendant(of: self.viewTextfield) {
  3706. self.containerPreviewReply.subviews.forEach { $0.removeFromSuperview() }
  3707. self.containerPreviewReply.removeConstraints(self.containerPreviewReply.constraints)
  3708. self.containerPreviewReply.removeFromSuperview()
  3709. self.reffId = nil
  3710. UIView.animate(withDuration: 0.25, delay: 0.0, options: .curveEaseInOut, animations: {
  3711. self.constraintTopTextField.constant = self.constraintTopTextField.constant - 50
  3712. }, completion: nil)
  3713. }
  3714. }
  3715. @objc func removeLinkPreviewUntilEmptyTextView() {
  3716. isAlwaysHideLinkPreview = true
  3717. deleteLinkPreview()
  3718. }
  3719. @objc func deleteLinkPreview() {
  3720. if self.containerLink.isDescendant(of: self.viewTextfield) {
  3721. self.containerLink.subviews.forEach { $0.removeFromSuperview() }
  3722. self.containerLink.removeConstraints(self.containerLink.constraints)
  3723. self.containerLink.removeFromSuperview()
  3724. UIView.animate(withDuration: 0.25, delay: 0.0, options: .curveEaseInOut, animations: {
  3725. self.constraintTopTextField.constant = self.constraintTopTextField.constant - 80
  3726. }, completion: nil)
  3727. self.showingLink = ""
  3728. }
  3729. if self.reffId != nil {
  3730. self.bottomAnchorPreviewReply.isActive = false
  3731. self.bottomAnchorPreviewReply = self.containerPreviewReply.bottomAnchor.constraint(equalTo: self.textFieldSend.topAnchor)
  3732. self.bottomAnchorPreviewReply.isActive = true
  3733. }
  3734. }
  3735. }
  3736. //ECL
  3737. extension EditorPersonal: UICollectionViewDelegate, UICollectionViewDataSource {
  3738. public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
  3739. return 76
  3740. }
  3741. public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
  3742. let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellSticker", for: indexPath)
  3743. if (cell.contentView.subviews.count > 0) {
  3744. cell.contentView.subviews.forEach({ $0.removeFromSuperview() })
  3745. }
  3746. let imageSticker = UIImageView()
  3747. cell.contentView.addSubview(imageSticker)
  3748. imageSticker.translatesAutoresizingMaskIntoConstraints = false
  3749. NSLayoutConstraint.activate([
  3750. imageSticker.topAnchor.constraint(equalTo: cell.contentView.topAnchor),
  3751. imageSticker.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor),
  3752. imageSticker.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor),
  3753. imageSticker.trailingAnchor.constraint(equalTo: cell.contentView.trailingAnchor)
  3754. ])
  3755. imageSticker.image = UIImage(named: stickers[indexPath.row], in: Bundle.resourcesMediaBundle(for: Nexilis.self), with: nil)
  3756. return cell
  3757. }
  3758. public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
  3759. sendChat(message_text: "sticker/\(stickers[indexPath.row])", attachment_flag: "11", viewController: self)
  3760. constraintBottomAttachment.constant = 0.0
  3761. self.viewSticker.removeConstraints(self.viewSticker.constraints)
  3762. self.viewSticker.removeFromSuperview()
  3763. }
  3764. }
  3765. //ETB
  3766. extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
  3767. // public func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
  3768. // checkNewMessage(tableView: tableView)
  3769. // }
  3770. public func scrollViewDidScroll(_ scrollView: UIScrollView) {
  3771. checkNewMessage(tableView: self.tableChatView)
  3772. }
  3773. public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
  3774. if isContactCenter && indexPath.row == 0 && isRequestContactCenter {
  3775. return
  3776. }
  3777. let dataMessages = self.dataMessages.filter({ $0["chat_date"] as! String == dataDates[indexPath.section] })
  3778. if copySession || forwardSession || deleteSession {
  3779. if (dataMessages[indexPath.row]["attachment_flag"] as! String != "0" || dataMessages[indexPath.row]["lock"] as? String == "1") && !forwardSession && !deleteSession {
  3780. return
  3781. }
  3782. 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 {
  3783. var file = dataMessages[indexPath.row]["image_id"] as! String
  3784. if file.isEmpty {
  3785. file = dataMessages[indexPath.row]["video_id"] as! String
  3786. if file.isEmpty {
  3787. file = dataMessages[indexPath.row]["file_id"] as! String
  3788. }
  3789. }
  3790. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  3791. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  3792. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  3793. if let dirPath = paths.first {
  3794. let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(file)
  3795. if !FileManager.default.fileExists(atPath: fileURL.path) {
  3796. return
  3797. }
  3798. }
  3799. }
  3800. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[indexPath.row]["message_id"] as? String})
  3801. if idx != nil {
  3802. self.dataMessages[idx!]["isSelected"] = !(self.dataMessages[idx!]["isSelected"] as! Bool)
  3803. self.tableChatView.reloadRows(at: [indexPath], with: .none)
  3804. }
  3805. containerMultpileSelectSession.subviews.forEach({ $0.removeFromSuperview() })
  3806. addSubviewMultipleSession()
  3807. return
  3808. }
  3809. let message = dataMessages[indexPath.row]
  3810. if let attachmentFlag = message["attachment_flag"], let attachmentFlag = attachmentFlag as? String {
  3811. if attachmentFlag == "27" || attachmentFlag == "26" {
  3812. let streamingController = (attachmentFlag == "27") ? QmeraCreateStreamingViewController() : CreateSeminarViewController()
  3813. if let messageText = message["message_text"],
  3814. let messageText = messageText as? String,
  3815. var json = try! JSONSerialization.jsonObject(with: messageText.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
  3816. if json["blog"] == nil {
  3817. json["blog"] = message["blog_id"] ?? nil
  3818. }
  3819. switch(attachmentFlag){
  3820. case "27":
  3821. (streamingController as! QmeraCreateStreamingViewController).data = json
  3822. default:
  3823. (streamingController as! CreateSeminarViewController).data = json
  3824. }
  3825. if json["by"] as? String != UserDefaults.standard.string(forKey: "me") as String? {
  3826. switch(attachmentFlag){
  3827. case "27":
  3828. (streamingController as! QmeraCreateStreamingViewController).isJoin = true
  3829. default:
  3830. (streamingController as! CreateSeminarViewController).isJoin = true
  3831. }
  3832. }
  3833. }
  3834. let streamingNav = UINavigationController(rootViewController: streamingController)
  3835. streamingNav.modalPresentationStyle = .custom
  3836. streamingNav.navigationBar.tintColor = .white
  3837. streamingNav.navigationBar.barTintColor = .mainColor
  3838. streamingNav.navigationBar.isTranslucent = false
  3839. streamingNav.navigationBar.overrideUserInterfaceStyle = .dark
  3840. streamingNav.navigationBar.barStyle = .black
  3841. let cancelButtonAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.font : UIFont.systemFont(ofSize: 16)]
  3842. UIBarButtonItem.appearance().setTitleTextAttributes(cancelButtonAttributes, for: .normal)
  3843. let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
  3844. streamingNav.navigationBar.titleTextAttributes = textAttributes
  3845. streamingNav.view.backgroundColor = .mainColor
  3846. streamingNav.navigationBar.isTranslucent = false
  3847. navigationController?.present(streamingNav, animated: true, completion: nil)
  3848. } else if message["message_scope_id"] as? String == "18" {
  3849. let formView = FormEditor()
  3850. let messageText = message["message_text"] as! String
  3851. formView.jsonData = messageText
  3852. formView.dataMessage = message
  3853. formView.dataPerson = self.dataPerson
  3854. formView.modalPresentationStyle = .custom
  3855. formView.modalTransitionStyle = .crossDissolve
  3856. formView.view.backgroundColor = .black.withAlphaComponent(0.2)
  3857. self.present(formView, animated: true, completion: nil)
  3858. }
  3859. }
  3860. }
  3861. public func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
  3862. let containerView = UIView()
  3863. containerView.backgroundColor = .clear
  3864. let dateView = UIView()
  3865. containerView.addSubview(dateView)
  3866. dateView.translatesAutoresizingMaskIntoConstraints = false
  3867. var topAnchor = dateView.topAnchor.constraint(equalTo: containerView.topAnchor)
  3868. if section == 0 {
  3869. topAnchor = dateView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 10.0)
  3870. }
  3871. NSLayoutConstraint.activate([
  3872. topAnchor,
  3873. dateView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
  3874. dateView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor),
  3875. dateView.heightAnchor.constraint(equalToConstant: 30),
  3876. dateView.widthAnchor.constraint(greaterThanOrEqualToConstant: 60)
  3877. ])
  3878. dateView.backgroundColor = .orangeColor
  3879. dateView.layer.cornerRadius = 15.0
  3880. dateView.clipsToBounds = true
  3881. let labelDate = UILabel()
  3882. dateView.addSubview(labelDate)
  3883. labelDate.translatesAutoresizingMaskIntoConstraints = false
  3884. NSLayoutConstraint.activate([
  3885. labelDate.centerYAnchor.constraint(equalTo: dateView.centerYAnchor),
  3886. labelDate.centerXAnchor.constraint(equalTo: dateView.centerXAnchor),
  3887. labelDate.leadingAnchor.constraint(equalTo: dateView.leadingAnchor, constant: 10),
  3888. labelDate.trailingAnchor.constraint(equalTo: dateView.trailingAnchor, constant: -10),
  3889. ])
  3890. labelDate.textAlignment = .center
  3891. labelDate.textColor = .secondaryColor
  3892. labelDate.font = UIFont.systemFont(ofSize: 12, weight: .medium)
  3893. labelDate.text = dataDates[section]
  3894. if listViewOnSection.count == 0 || listViewOnSection.count - 1 < section {
  3895. listViewOnSection.append(containerView)
  3896. } else {
  3897. listViewOnSection.remove(at: section)
  3898. listViewOnSection.insert(containerView, at: section)
  3899. }
  3900. return containerView
  3901. }
  3902. public func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
  3903. if section == 0 {
  3904. return 40
  3905. }
  3906. return 30
  3907. }
  3908. public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  3909. let idMe = UserDefaults.standard.string(forKey: "me") as String?
  3910. let dataMessages = dataMessages.filter({$0["chat_date"] as! String == dataDates[indexPath.section]})
  3911. let profileMessage = UIImageView()
  3912. let cell = tableView.dequeueReusableCell(withIdentifier: "cellEditorPersonal", for: indexPath as IndexPath)
  3913. cell.contentView.subviews.forEach({ $0.removeFromSuperview() })
  3914. if isContactCenter && isRequestContactCenter && dataMessages[indexPath.row]["category_cc"] != nil {
  3915. cell.backgroundColor = .clear
  3916. cell.selectionStyle = .none
  3917. if dataMessages[indexPath.row]["category_cc"] is [CategoryCC] {
  3918. let category_cc = dataMessages[indexPath.row]["category_cc"] as! [CategoryCC]
  3919. profileMessage.frame.size = CGSize(width: 35, height: 35)
  3920. cell.contentView.addSubview(profileMessage)
  3921. profileMessage.translatesAutoresizingMaskIntoConstraints = false
  3922. profileMessage.topAnchor.constraint(equalTo: cell.contentView.topAnchor, constant: 5).isActive = true
  3923. profileMessage.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: 15).isActive = true
  3924. profileMessage.heightAnchor.constraint(equalToConstant: 37).isActive = true
  3925. profileMessage.widthAnchor.constraint(equalToConstant: 35).isActive = true
  3926. profileMessage.circle()
  3927. profileMessage.clipsToBounds = true
  3928. profileMessage.backgroundColor = .lightGray
  3929. profileMessage.image = UIImage(systemName: "person")
  3930. profileMessage.tintColor = .white
  3931. profileMessage.contentMode = .scaleAspectFit
  3932. getImage(name: dataPerson["picture"]!!, placeholderImage: UIImage(systemName: "person.circle.fill")!) { result, isDownloaded, image in
  3933. profileMessage.image = image
  3934. }
  3935. profileMessage.contentMode = .scaleAspectFill
  3936. let containerMessage = UIView()
  3937. cell.contentView.addSubview(containerMessage)
  3938. containerMessage.translatesAutoresizingMaskIntoConstraints = false
  3939. containerMessage.topAnchor.constraint(equalTo: cell.contentView.topAnchor, constant: 5).isActive = true
  3940. containerMessage.leadingAnchor.constraint(equalTo: profileMessage.trailingAnchor, constant: 5).isActive = true
  3941. containerMessage.trailingAnchor.constraint(lessThanOrEqualTo: cell.contentView.trailingAnchor, constant: -60).isActive = true
  3942. containerMessage.widthAnchor.constraint(greaterThanOrEqualToConstant: 46).isActive = true
  3943. containerMessage.backgroundColor = .grayColor
  3944. containerMessage.layer.cornerRadius = 10.0
  3945. containerMessage.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMinYCorner, .layerMaxXMaxYCorner]
  3946. containerMessage.clipsToBounds = true
  3947. let timeMessage = UILabel()
  3948. cell.contentView.addSubview(timeMessage)
  3949. timeMessage.translatesAutoresizingMaskIntoConstraints = false
  3950. timeMessage.leadingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: 8).isActive = true
  3951. let messageText = UILabel()
  3952. containerMessage.addSubview(messageText)
  3953. messageText.translatesAutoresizingMaskIntoConstraints = false
  3954. messageText.numberOfLines = 0
  3955. messageText.lineBreakMode = .byWordWrapping
  3956. containerMessage.addSubview(messageText)
  3957. messageText.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 15).isActive = true
  3958. messageText.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
  3959. messageText.bottomAnchor.constraint(equalTo: containerMessage.bottomAnchor, constant: -15).isActive = true
  3960. messageText.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  3961. if category_cc[0].id.contains("level0_") || dataMessages[indexPath.row]["attachment_flag"] != nil && dataMessages[indexPath.row]["attachment_flag"] as! String == "503" {
  3962. messageText.text = "Welcome to".localized() + " " + dataPerson["name"]!! + " " + "Contact Center".localized()
  3963. // + "\n" + "\n" + "Please select the communication media do you want".localized()
  3964. } else if category_cc[0].id.contains("level1_") {
  3965. messageText.text = "Please select your Consultation Topic:".localized()
  3966. } else if !category_cc[0].id.contains("level1_") && dataMessages[indexPath.row]["attachment_flag"] == nil {
  3967. messageText.text = "Please select the type of topic that you chosen".localized()
  3968. } else if dataMessages[indexPath.row]["attachment_flag"] != nil && dataMessages[indexPath.row]["attachment_flag"] as! String == "502" {
  3969. messageText.text = "Please select the information option:".localized()
  3970. } else {
  3971. 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()
  3972. }
  3973. messageText.font = UIFont.italicSystemFont(ofSize: 12)
  3974. messageText.textColor = .black
  3975. let date = Date()
  3976. let formatter = DateFormatter()
  3977. formatter.dateFormat = "HH:mm"
  3978. formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
  3979. timeMessage.text = formatter.string(from: date as Date)
  3980. timeMessage.font = UIFont.systemFont(ofSize: 10, weight: .medium)
  3981. timeMessage.textColor = .lightGray
  3982. let containerButton = UIView()
  3983. cell.contentView.addSubview(containerButton)
  3984. containerButton.translatesAutoresizingMaskIntoConstraints = false
  3985. containerButton.topAnchor.constraint(equalTo: containerMessage.bottomAnchor, constant: 5).isActive = true
  3986. containerButton.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor, constant: -5).isActive = true
  3987. containerButton.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: 55).isActive = true
  3988. containerButton.widthAnchor.constraint(equalToConstant: self.view!.frame.size.width * 0.65).isActive = true
  3989. containerButton.heightAnchor.constraint(greaterThanOrEqualToConstant: 45).isActive = true
  3990. containerButton.backgroundColor = .clear
  3991. timeMessage.bottomAnchor.constraint(equalTo:containerButton.topAnchor, constant: -5).isActive = true
  3992. for i in 0..<category_cc.count {
  3993. let buttonChat = UIButton()
  3994. containerButton.addSubview(buttonChat)
  3995. buttonChat.translatesAutoresizingMaskIntoConstraints = false
  3996. buttonChat.widthAnchor.constraint(equalToConstant: self.view!.frame.size.width * 0.65 / 2 - 10).isActive = true
  3997. buttonChat.heightAnchor.constraint(greaterThanOrEqualToConstant: 45).isActive = true
  3998. if i % 2 == 0 {
  3999. if i / 2 + 1 == 1 {
  4000. buttonChat.topAnchor.constraint(equalTo: containerButton.topAnchor, constant: 5).isActive = true
  4001. } else {
  4002. var constantTop = (i / 2 - 1) * 50
  4003. if constantTop == 0 {
  4004. constantTop = 55
  4005. } else {
  4006. constantTop = constantTop + 55
  4007. }
  4008. buttonChat.topAnchor.constraint(equalTo: containerButton.topAnchor, constant: CGFloat(constantTop)).isActive = true
  4009. }
  4010. if i == category_cc.count - 1 {
  4011. buttonChat.bottomAnchor.constraint(equalTo: containerButton.bottomAnchor, constant: -5).isActive = true
  4012. }
  4013. buttonChat.leadingAnchor.constraint(equalTo: containerButton.leadingAnchor, constant: 5).isActive = true
  4014. } else {
  4015. let newi = i - 1
  4016. if newi / 2 + 1 == 1 {
  4017. buttonChat.topAnchor.constraint(equalTo: containerButton.topAnchor, constant: 5).isActive = true
  4018. } else {
  4019. var constantTop = (newi / 2 - 1) * 50
  4020. if constantTop == 0 {
  4021. constantTop = 55
  4022. } else {
  4023. constantTop = constantTop + 55
  4024. }
  4025. buttonChat.topAnchor.constraint(equalTo: containerButton.topAnchor, constant: CGFloat(constantTop)).isActive = true
  4026. }
  4027. if i == category_cc.count - 1 {
  4028. buttonChat.bottomAnchor.constraint(equalTo: containerButton.bottomAnchor, constant: -5).isActive = true
  4029. }
  4030. buttonChat.trailingAnchor.constraint(equalTo: containerButton.trailingAnchor, constant: -5).isActive = true
  4031. }
  4032. if category_cc[i].isActive {
  4033. buttonChat.backgroundColor = .orangeBNI
  4034. }
  4035. buttonChat.setTitle(category_cc[i].service_name.localized(), for: .normal)
  4036. buttonChat.setTitleColor(.black, for: .normal)
  4037. buttonChat.titleLabel?.font = UIFont.systemFont(ofSize: 12)
  4038. buttonChat.titleLabel?.numberOfLines = 0
  4039. buttonChat.titleLabel?.textAlignment = .center
  4040. buttonChat.layer.borderWidth = 1
  4041. buttonChat.layer.borderColor = UIColor.lightGray.withAlphaComponent(0.3).cgColor
  4042. buttonChat.restorationIdentifier = "\(category_cc[i].id),\(category_cc[i].service_id)"
  4043. if dataMessages[indexPath.row]["attachment_flag"] != nil {
  4044. buttonChat.tag = Int(dataMessages[indexPath.row]["attachment_flag"] as! String)!
  4045. }
  4046. buttonChat.addTarget(self, action: #selector(ccAction(sender:)), for: .touchUpInside)
  4047. }
  4048. } else {
  4049. let messageWait = UILabel()
  4050. cell.contentView.addSubview(messageWait)
  4051. messageWait.translatesAutoresizingMaskIntoConstraints = false
  4052. messageWait.topAnchor.constraint(equalTo: cell.contentView.topAnchor).isActive = true
  4053. messageWait.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor).isActive = true
  4054. messageWait.leftAnchor.constraint(equalTo: cell.contentView.leftAnchor, constant: 10).isActive = true
  4055. messageWait.rightAnchor.constraint(equalTo: cell.contentView.rightAnchor, constant: -10).isActive = true
  4056. messageWait.text = dataMessages[indexPath.row]["category_cc"] as? String ?? dataMessages[indexPath.row]["message_text"] as? String ?? ""
  4057. messageWait.numberOfLines = 0
  4058. messageWait.font = UIFont.systemFont(ofSize: 12)
  4059. messageWait.textColor = .gray
  4060. messageWait.textAlignment = .center
  4061. }
  4062. return cell
  4063. }
  4064. let thumbChat = (dataMessages[indexPath.row]["thumb_id"] as? String) ?? ""
  4065. let imageChat = (dataMessages[indexPath.row]["image_id"] as? String) ?? ""
  4066. let videoChat = (dataMessages[indexPath.row]["video_id"] as? String) ?? ""
  4067. let fileChat = (dataMessages[indexPath.row]["file_id"] as? String) ?? ""
  4068. let reffChat = (dataMessages[indexPath.row]["reff_id"] as? String) ?? ""
  4069. let dataTimer = listTimerCredential[(dataMessages[indexPath.row]["message_id"] as! String)]
  4070. cell.backgroundColor = .clear
  4071. cell.selectionStyle = .none
  4072. let nameSender = UILabel()
  4073. if isContactCenter {
  4074. profileMessage.frame.size = CGSize(width: 35, height: 35)
  4075. cell.contentView.addSubview(profileMessage)
  4076. profileMessage.translatesAutoresizingMaskIntoConstraints = false
  4077. profileMessage.topAnchor.constraint(equalTo: cell.contentView.topAnchor, constant: 5).isActive = true
  4078. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  4079. profileMessage.trailingAnchor.constraint(equalTo: cell.contentView.trailingAnchor, constant: -15).isActive = true
  4080. } else {
  4081. if copySession || forwardSession || deleteSession {
  4082. profileMessage.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: 50).isActive = true
  4083. } else {
  4084. profileMessage.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: 15).isActive = true
  4085. }
  4086. }
  4087. profileMessage.heightAnchor.constraint(equalToConstant: 37).isActive = true
  4088. profileMessage.widthAnchor.constraint(equalToConstant: 35).isActive = true
  4089. profileMessage.circle()
  4090. profileMessage.clipsToBounds = true
  4091. profileMessage.backgroundColor = .lightGray
  4092. profileMessage.image = UIImage(systemName: "person")
  4093. profileMessage.tintColor = .white
  4094. profileMessage.contentMode = .scaleAspectFit
  4095. let user = User.getData(pin: dataMessages[indexPath.row]["f_pin"] as? String)
  4096. getImage(name: user?.thumb ?? "", placeholderImage: UIImage(systemName: "person.circle.fill")!, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
  4097. profileMessage.image = image
  4098. }
  4099. profileMessage.contentMode = .scaleAspectFill
  4100. cell.contentView.addSubview(nameSender)
  4101. nameSender.translatesAutoresizingMaskIntoConstraints = false
  4102. if markerCounter != nil && dataMessages[indexPath.row]["message_id"] as? String == markerCounter {
  4103. nameSender.topAnchor.constraint(equalTo: cell.contentView.topAnchor, constant: 35).isActive = true
  4104. } else {
  4105. nameSender.topAnchor.constraint(equalTo: cell.contentView.topAnchor, constant: 5).isActive = true
  4106. }
  4107. nameSender.font = UIFont.systemFont(ofSize: 12, weight: UIFont.Weight(800))
  4108. nameSender.text = user?.fullName ?? ""
  4109. nameSender.textAlignment = .right
  4110. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  4111. nameSender.trailingAnchor.constraint(equalTo:profileMessage.leadingAnchor, constant: -5).isActive = true
  4112. nameSender.textColor = .systemBlue
  4113. } else {
  4114. nameSender.leadingAnchor.constraint(equalTo:profileMessage.trailingAnchor, constant: 5).isActive = true
  4115. nameSender.textColor = .orangeColor
  4116. }
  4117. }
  4118. let containerMessage = UIView()
  4119. cell.contentView.addSubview(containerMessage)
  4120. containerMessage.translatesAutoresizingMaskIntoConstraints = false
  4121. let timeMessage = UILabel()
  4122. cell.contentView.addSubview(timeMessage)
  4123. timeMessage.translatesAutoresizingMaskIntoConstraints = false
  4124. if (dataMessages[indexPath.row]["read_receipts"] as? String) == "8" || ((dataMessages[indexPath.row]["credential"] as? String) == "1" && dataMessages[indexPath.row]["lock"] as? String != "2") {
  4125. timeMessage.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor, constant: -40).isActive = true
  4126. } else {
  4127. timeMessage.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor, constant: -5).isActive = true
  4128. }
  4129. let statusMessage = UIImageView()
  4130. if (dataMessages[indexPath.row]["attachment_flag"] as? String == "0" && dataMessages[indexPath.row]["lock"] as? String != "1") || forwardSession || deleteSession {
  4131. var showSelectedImage = true
  4132. if (!imageChat.isEmpty || !videoChat.isEmpty || !fileChat.isEmpty) && forwardSession {
  4133. var file = dataMessages[indexPath.row]["image_id"] as! String
  4134. if file.isEmpty {
  4135. file = dataMessages[indexPath.row]["video_id"] as! String
  4136. if file.isEmpty {
  4137. file = dataMessages[indexPath.row]["file_id"] as! String
  4138. }
  4139. }
  4140. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  4141. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  4142. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  4143. if let dirPath = paths.first {
  4144. let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(file)
  4145. if !FileManager.default.fileExists(atPath: fileURL.path) {
  4146. showSelectedImage = false
  4147. }
  4148. }
  4149. }
  4150. if showSelectedImage {
  4151. let selectedImage = UIImageView()
  4152. cell.contentView.addSubview(selectedImage)
  4153. selectedImage.translatesAutoresizingMaskIntoConstraints = false
  4154. selectedImage.frame.size = CGSize(width: 20, height: 20)
  4155. var leading = selectedImage.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: -20)
  4156. selectedImage.isHidden = true
  4157. if copySession || forwardSession || deleteSession {
  4158. leading = selectedImage.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: 15)
  4159. selectedImage.isHidden = false
  4160. }
  4161. NSLayoutConstraint.activate([
  4162. leading,
  4163. selectedImage.centerYAnchor.constraint(equalTo: cell.contentView.centerYAnchor),
  4164. selectedImage.widthAnchor.constraint(equalToConstant: 20),
  4165. selectedImage.heightAnchor.constraint(equalToConstant: 20)
  4166. ])
  4167. selectedImage.circle()
  4168. selectedImage.layer.borderWidth = 2
  4169. selectedImage.layer.borderColor = UIColor.mainColor.cgColor
  4170. if dataMessages[indexPath.row]["isSelected"] as! Bool {
  4171. selectedImage.image = UIImage(systemName: "checkmark.circle.fill")
  4172. }
  4173. selectedImage.tintColor = .mainColor
  4174. }
  4175. }
  4176. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  4177. containerMessage.leadingAnchor.constraint(greaterThanOrEqualTo: cell.contentView.leadingAnchor, constant: 60).isActive = true
  4178. if (dataMessages[indexPath.row]["read_receipts"] as? String) == "8" || ((dataMessages[indexPath.row]["credential"] as? String) == "1" && dataMessages[indexPath.row]["lock"] as? String != "2") {
  4179. containerMessage.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor, constant: -40).isActive = true
  4180. } else {
  4181. containerMessage.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor, constant: -5).isActive = true
  4182. }
  4183. if isContactCenter {
  4184. containerMessage.topAnchor.constraint(equalTo: nameSender.bottomAnchor).isActive = true
  4185. containerMessage.trailingAnchor.constraint(equalTo: profileMessage.leadingAnchor, constant: -5).isActive = true
  4186. } else {
  4187. containerMessage.topAnchor.constraint(equalTo: cell.contentView.topAnchor, constant: 5).isActive = true
  4188. containerMessage.trailingAnchor.constraint(equalTo: cell.contentView.trailingAnchor, constant: -15).isActive = true
  4189. }
  4190. containerMessage.widthAnchor.constraint(greaterThanOrEqualToConstant: 46).isActive = true
  4191. if (dataMessages[indexPath.row]["attachment_flag"] as? String == "11" && dataMessages[indexPath.row]["reff_id"]as? String == "") {
  4192. containerMessage.backgroundColor = .clear
  4193. } else {
  4194. containerMessage.backgroundColor = .blueBubbleColor
  4195. }
  4196. containerMessage.layer.cornerRadius = 10.0
  4197. containerMessage.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner, .layerMinXMinYCorner]
  4198. containerMessage.clipsToBounds = true
  4199. timeMessage.trailingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: -8).isActive = true
  4200. if (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as! String == "0") && (dataMessages[indexPath.row]["lock"] as? String != "2") {
  4201. cell.contentView.addSubview(statusMessage)
  4202. statusMessage.translatesAutoresizingMaskIntoConstraints = false
  4203. statusMessage.bottomAnchor.constraint(equalTo: timeMessage.topAnchor).isActive = true
  4204. statusMessage.trailingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: -8).isActive = true
  4205. statusMessage.widthAnchor.constraint(equalToConstant: 15).isActive = true
  4206. statusMessage.heightAnchor.constraint(equalToConstant: 15).isActive = true
  4207. if (dataMessages[indexPath.row]["status"]! as! String == "1" || dataMessages[indexPath.row]["status"]! as! String == "2" ) {
  4208. statusMessage.image = UIImage(named: "checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.lightGray)
  4209. } else if (dataMessages[indexPath.row]["status"]! as! String == "3") {
  4210. statusMessage.image = UIImage(named: "double-checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.lightGray)
  4211. } else if (dataMessages[indexPath.row]["status"]! as! String == "8") {
  4212. statusMessage.image = UIImage(named: "message_status_ack", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withRenderingMode(.alwaysOriginal)
  4213. } else {
  4214. statusMessage.image = UIImage(named: "double-checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.systemBlue)
  4215. }
  4216. }
  4217. } else {
  4218. if markerCounter != nil && dataMessages[indexPath.row]["message_id"] as? String == markerCounter {
  4219. if isContactCenter {
  4220. containerMessage.topAnchor.constraint(equalTo: nameSender.bottomAnchor).isActive = true
  4221. } else {
  4222. containerMessage.topAnchor.constraint(equalTo: cell.contentView.topAnchor, constant: 35).isActive = true
  4223. }
  4224. let newMessagesView = UIView()
  4225. cell.contentView.addSubview(newMessagesView)
  4226. newMessagesView.translatesAutoresizingMaskIntoConstraints = false
  4227. NSLayoutConstraint.activate([
  4228. newMessagesView.topAnchor.constraint(equalTo: newMessagesView.topAnchor),
  4229. newMessagesView.bottomAnchor.constraint(equalTo: containerMessage.topAnchor),
  4230. newMessagesView.centerXAnchor.constraint(equalTo: cell.contentView.centerXAnchor),
  4231. newMessagesView.heightAnchor.constraint(equalToConstant: 30),
  4232. newMessagesView.widthAnchor.constraint(greaterThanOrEqualToConstant: 60)
  4233. ])
  4234. newMessagesView.backgroundColor = .greenColor
  4235. newMessagesView.layer.cornerRadius = 15.0
  4236. newMessagesView.clipsToBounds = true
  4237. let labelNewMessages = UILabel()
  4238. newMessagesView.addSubview(labelNewMessages)
  4239. labelNewMessages.translatesAutoresizingMaskIntoConstraints = false
  4240. NSLayoutConstraint.activate([
  4241. labelNewMessages.centerYAnchor.constraint(equalTo: newMessagesView.centerYAnchor),
  4242. labelNewMessages.centerXAnchor.constraint(equalTo: newMessagesView.centerXAnchor),
  4243. labelNewMessages.leadingAnchor.constraint(equalTo: newMessagesView.leadingAnchor, constant: 10),
  4244. labelNewMessages.trailingAnchor.constraint(equalTo: newMessagesView.trailingAnchor, constant: -10),
  4245. ])
  4246. labelNewMessages.textAlignment = .center
  4247. labelNewMessages.textColor = .secondaryColor
  4248. labelNewMessages.font = UIFont.systemFont(ofSize: 12, weight: .medium)
  4249. labelNewMessages.text = "Unread Messages".localized()
  4250. } else {
  4251. if isContactCenter {
  4252. containerMessage.topAnchor.constraint(equalTo: nameSender.bottomAnchor).isActive = true
  4253. } else {
  4254. containerMessage.topAnchor.constraint(equalTo: cell.contentView.topAnchor, constant: 5).isActive = true
  4255. }
  4256. }
  4257. if isContactCenter {
  4258. containerMessage.leadingAnchor.constraint(equalTo: profileMessage.trailingAnchor, constant: 5).isActive = true
  4259. } else {
  4260. if copySession || forwardSession || deleteSession {
  4261. containerMessage.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: 50).isActive = true
  4262. } else {
  4263. containerMessage.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: 15).isActive = true
  4264. }
  4265. }
  4266. if (dataMessages[indexPath.row]["read_receipts"] as? String) == "8" || ((dataMessages[indexPath.row]["credential"] as? String) == "1" && dataMessages[indexPath.row]["lock"] as? String != "2") {
  4267. containerMessage.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor, constant: -40).isActive = true
  4268. } else {
  4269. containerMessage.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor, constant: -5).isActive = true
  4270. }
  4271. containerMessage.trailingAnchor.constraint(lessThanOrEqualTo: cell.contentView.trailingAnchor, constant: -60).isActive = true
  4272. containerMessage.widthAnchor.constraint(greaterThanOrEqualToConstant: 46).isActive = true
  4273. 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") {
  4274. containerMessage.backgroundColor = .clear
  4275. } else {
  4276. containerMessage.backgroundColor = .grayColor
  4277. }
  4278. containerMessage.layer.cornerRadius = 10.0
  4279. containerMessage.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMinYCorner, .layerMaxXMaxYCorner]
  4280. containerMessage.clipsToBounds = true
  4281. timeMessage.leadingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: 8).isActive = true
  4282. }
  4283. if dataMessages[indexPath.row]["is_stared"] as? String == "1" && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as! String == "0") {
  4284. let imageStared = UIImageView()
  4285. cell.contentView.addSubview(imageStared)
  4286. imageStared.translatesAutoresizingMaskIntoConstraints = false
  4287. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  4288. imageStared.bottomAnchor.constraint(equalTo: statusMessage.topAnchor).isActive = true
  4289. imageStared.trailingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: -8).isActive = true
  4290. } else {
  4291. imageStared.bottomAnchor.constraint(equalTo: timeMessage.topAnchor).isActive = true
  4292. imageStared.leadingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: 8).isActive = true
  4293. }
  4294. imageStared.widthAnchor.constraint(equalToConstant: 15).isActive = true
  4295. imageStared.heightAnchor.constraint(equalToConstant: 15).isActive = true
  4296. imageStared.image = UIImage(systemName: "star.fill")
  4297. imageStared.backgroundColor = .clear
  4298. imageStared.tintColor = .systemYellow
  4299. }
  4300. if dataMessages[indexPath.row]["read_receipts"] as? String == "8" {
  4301. let imageAckView = UIImageView()
  4302. var imageAck = UIImage(named: "ack_icon_gray", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withRenderingMode(.alwaysOriginal)
  4303. if dataMessages[indexPath.row]["status"] as? String == "8" {
  4304. imageAck = UIImage(named: "ack_icon", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withRenderingMode(.alwaysOriginal)
  4305. }
  4306. imageAckView.image = imageAck
  4307. cell.contentView.addSubview(imageAckView)
  4308. imageAckView.translatesAutoresizingMaskIntoConstraints = false
  4309. imageAckView.widthAnchor.constraint(equalToConstant: 30).isActive = true
  4310. imageAckView.heightAnchor.constraint(equalToConstant: 30).isActive = true
  4311. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  4312. imageAckView.topAnchor.constraint(equalTo: containerMessage.bottomAnchor, constant: 5).isActive = true
  4313. imageAckView.trailingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 30).isActive = true
  4314. } else {
  4315. imageAckView.topAnchor.constraint(equalTo: containerMessage.bottomAnchor, constant: 5).isActive = true
  4316. imageAckView.leadingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -30).isActive = true
  4317. let tap = ObjectGesture(target: self, action: #selector(tapAck(_:)))
  4318. tap.indexPath = indexPath
  4319. imageAckView.addGestureRecognizer(tap)
  4320. imageAckView.isUserInteractionEnabled = true
  4321. }
  4322. }
  4323. if (dataMessages[indexPath.row]["credential"] as? String) == "1" && (dataMessages[indexPath.row]["lock"] as? String) != "2" {
  4324. let imageCredentialView = UIImageView()
  4325. let imageCredential = UIImage(named: "confidential_icon", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withRenderingMode(.alwaysOriginal)
  4326. imageCredentialView.image = imageCredential
  4327. cell.contentView.addSubview(imageCredentialView)
  4328. imageCredentialView.translatesAutoresizingMaskIntoConstraints = false
  4329. imageCredentialView.widthAnchor.constraint(equalToConstant: 30).isActive = true
  4330. imageCredentialView.heightAnchor.constraint(equalToConstant: 30).isActive = true
  4331. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  4332. imageCredentialView.topAnchor.constraint(equalTo: containerMessage.bottomAnchor, constant: 5).isActive = true
  4333. imageCredentialView.trailingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 30).isActive = true
  4334. } else {
  4335. imageCredentialView.topAnchor.constraint(equalTo: containerMessage.bottomAnchor, constant: 5).isActive = true
  4336. imageCredentialView.leadingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -30).isActive = true
  4337. }
  4338. }
  4339. let messageText = UILabel()
  4340. messageText.numberOfLines = 0
  4341. messageText.lineBreakMode = .byWordWrapping
  4342. containerMessage.addSubview(messageText)
  4343. messageText.translatesAutoresizingMaskIntoConstraints = false
  4344. let topMarginText = messageText.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 15)
  4345. topMarginText.isActive = true
  4346. messageText.textColor = .black
  4347. 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" {
  4348. messageText.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 85).isActive = true
  4349. let imageLS = UIImageView()
  4350. containerMessage.addSubview(imageLS)
  4351. imageLS.translatesAutoresizingMaskIntoConstraints = false
  4352. NSLayoutConstraint.activate([
  4353. imageLS.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15.0),
  4354. imageLS.trailingAnchor.constraint(equalTo: messageText.leadingAnchor, constant: -10.0),
  4355. imageLS.centerYAnchor.constraint(equalTo: containerMessage.centerYAnchor),
  4356. imageLS.heightAnchor.constraint(equalToConstant: 60.0)
  4357. ])
  4358. if dataMessages[indexPath.row]["attachment_flag"] as! String == "26" {
  4359. imageLS.image = UIImage(named: "pb_seminar_wpr", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  4360. } else if dataMessages[indexPath.row]["attachment_flag"] as! String == "27" {
  4361. imageLS.image = UIImage(named: "pb_live_tv", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  4362. } else if dataMessages[indexPath.row]["message_scope_id"] as? String == "18" {
  4363. imageLS.image = UIImage(systemName: "doc.richtext.fill")
  4364. imageLS.tintColor = .mainColor
  4365. }
  4366. } else {
  4367. messageText.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
  4368. }
  4369. 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") {
  4370. messageText.bottomAnchor.constraint(equalTo: containerMessage.bottomAnchor, constant: -115).isActive = true
  4371. let imageQR = UIImageView()
  4372. containerMessage.addSubview(imageQR)
  4373. imageQR.translatesAutoresizingMaskIntoConstraints = false
  4374. NSLayoutConstraint.activate([
  4375. imageQR.centerXAnchor.constraint(equalTo: containerMessage.centerXAnchor),
  4376. imageQR.topAnchor.constraint(equalTo: messageText.bottomAnchor),
  4377. imageQR.widthAnchor.constraint(equalToConstant: 100.0),
  4378. imageQR.heightAnchor.constraint(equalToConstant: 100.0)
  4379. ])
  4380. imageQR.image = generateQRCode(from: dataMessages[indexPath.row]["blog_id"] as! String)
  4381. } else {
  4382. messageText.bottomAnchor.constraint(equalTo: containerMessage.bottomAnchor, constant: -15).isActive = true
  4383. }
  4384. messageText.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  4385. var textChat = (dataMessages[indexPath.row]["message_text"] as? String) ?? ""
  4386. if (dataMessages[indexPath.row]["lock"] != nil && (dataMessages[indexPath.row]["lock"])! as? String == "1") {
  4387. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  4388. textChat = "🚫 _"+"You were deleted this message".localized()+"_"
  4389. } else {
  4390. textChat = "🚫 _"+"This message was deleted".localized()+"_"
  4391. }
  4392. }
  4393. if dataMessages[indexPath.row]["lock"] as? String == "2" {
  4394. textChat = "🚫 _"+"Message has expired".localized()+"_"
  4395. }
  4396. let imageSticker = UIImageView()
  4397. if let attachmentFlag = dataMessages[indexPath.row]["attachment_flag"], let attachmentFlag = attachmentFlag as? String {
  4398. if attachmentFlag == "27" || attachmentFlag == "26" { // live streaming
  4399. let data = textChat
  4400. if let json = try! JSONSerialization.jsonObject(with: data.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
  4401. Database().database?.inTransaction({ fmdb, rollback in
  4402. let title = json["title"] as? String ?? ""
  4403. let description = json["description"] as? String ?? ""
  4404. let start = json["time"] as? Int64 ?? 0
  4405. let by = json["by"] as? String ?? ""
  4406. let textLS = "Live Streaming".localized()
  4407. var type = "*\(textLS)*"
  4408. if attachmentFlag == "26" {
  4409. let textSeminar = "Seminar".localized()
  4410. type = "*\(textSeminar)*"
  4411. }
  4412. if let c = Database().getRecords(fmdb: fmdb, query: "select first_name || ' ' || last_name from BUDDY where f_pin = '\(by)'"), c.next() {
  4413. let name = c.string(forColumnIndex: 0)!
  4414. messageText.attributedText = "\(type) \nTitle: \(title) \nDescription: \(description) \nStart: \(Date(milliseconds: start).format(dateFormat: "dd/MM/yyyy HH:mm")) \nBroadcaster: \(name)".richText()
  4415. c.close()
  4416. } else {
  4417. messageText.attributedText = ("\(type) \nTitle: \(title) \nDescription: \(description) \nStart: \(Date(milliseconds: start).format(dateFormat: "dd/MM/yyyy HH:mm"))").richText()
  4418. }
  4419. })
  4420. }
  4421. }
  4422. else if attachmentFlag == "11" && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as! String != "1") && (dataMessages[indexPath.row]["lock"] as? String != "2") {
  4423. messageText.text = ""
  4424. topMarginText.constant = topMarginText.constant + 100
  4425. containerMessage.addSubview(imageSticker)
  4426. imageSticker.translatesAutoresizingMaskIntoConstraints = false
  4427. if (reffChat == "") {
  4428. imageSticker.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 15).isActive = true
  4429. imageSticker.widthAnchor.constraint(equalToConstant: 80).isActive = true
  4430. } else {
  4431. imageSticker.widthAnchor.constraint(greaterThanOrEqualToConstant: 80).isActive = true
  4432. }
  4433. imageSticker.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
  4434. imageSticker.bottomAnchor.constraint(equalTo: messageText.topAnchor, constant: -5).isActive = true
  4435. imageSticker.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  4436. imageSticker.image = UIImage(named: (textChat.components(separatedBy: "/")[1]), in: Bundle.resourcesMediaBundle(for: Nexilis.self), with: nil)
  4437. imageSticker.contentMode = .scaleAspectFit
  4438. } else if dataMessages[indexPath.row]["message_scope_id"] as! String == "18" {
  4439. let data = textChat
  4440. if let jsonForm = try! JSONSerialization.jsonObject(with: data.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
  4441. let form_title = jsonForm["form_title"] as! String
  4442. let club_type = jsonForm["club_type"] as! String
  4443. let province = jsonForm["province"] as! String
  4444. let club = jsonForm["club"] as! String
  4445. messageText.attributedText = "*\(form_title.replacingOccurrences(of: "+", with: " "))* \nClub Type: \(club_type) \nProvince: \(province) \nClub Name: \(club) ".richText()
  4446. }
  4447. }
  4448. else {
  4449. messageText.attributedText = textChat.richText()
  4450. }
  4451. } else {
  4452. messageText.attributedText = textChat.richText()
  4453. }
  4454. messageText.isUserInteractionEnabled = false
  4455. if !textChat.isEmpty {
  4456. let listTextEnter = textChat.split(separator: "\n")
  4457. for j in 0...listTextEnter.count - 1 {
  4458. let listText = listTextEnter[j].split(separator: " ")
  4459. for i in 0...listText.count - 1 {
  4460. if listText[i].lowercased().checkStartWithLink() {
  4461. let rangeTapLink = (textChat as NSString).range(of: String(listText[i]))
  4462. // Add tap gesture recognizer to the range of text
  4463. let attributedString = textChat.richText()
  4464. attributedString.addAttributes([.foregroundColor: UIColor.blue, .underlineStyle: NSUnderlineStyle.single.rawValue], range: rangeTapLink)
  4465. messageText.attributedText = attributedString
  4466. if messageText.isUserInteractionEnabled == false && !copySession && !forwardSession && !deleteSession && !self.removed {
  4467. messageText.isUserInteractionEnabled = true
  4468. let longPress = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPressLink(_:)))
  4469. longPress.minimumPressDuration = 0.1
  4470. containerMessage.addGestureRecognizer(longPress)
  4471. }
  4472. }
  4473. }
  4474. }
  4475. }
  4476. if !copySession && !forwardSession && !deleteSession && !self.removed && messageText.isUserInteractionEnabled == false {
  4477. let interaction = UIContextMenuInteraction(delegate: self)
  4478. containerMessage.addInteraction(interaction)
  4479. containerMessage.isUserInteractionEnabled = true
  4480. }
  4481. if isSearching && textSearch.count > 1 {
  4482. messageText.attributedText = textChat.richText(isSearching: true, textSearch: textSearch)
  4483. if textChat.lowercased().contains(textSearch) {
  4484. countMatchesSearch += 1
  4485. }
  4486. }
  4487. let stringDate = (dataMessages[indexPath.row]["server_date"] as? String) ?? ""
  4488. if !stringDate.isEmpty {
  4489. if (dataMessages[indexPath.row]["credential"] as? String) == "1" && dataMessages[indexPath.row]["lock"] as? String != "2" {
  4490. if dataTimer! >= 10 {
  4491. timeMessage.text = "00:\(dataTimer!)"
  4492. } else {
  4493. timeMessage.text = "00:0\(dataTimer!)"
  4494. }
  4495. timeMessage.textColor = .systemRed
  4496. } else {
  4497. let date = Date(milliseconds: Int64(stringDate) ?? 100)
  4498. let formatter = DateFormatter()
  4499. formatter.dateFormat = "HH:mm"
  4500. formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
  4501. timeMessage.text = formatter.string(from: date as Date)
  4502. timeMessage.textColor = .lightGray
  4503. }
  4504. timeMessage.font = UIFont.systemFont(ofSize: 10, weight: .medium)
  4505. }
  4506. let imageThumb = UIImageView()
  4507. let containerViewFile = UIView()
  4508. if (!thumbChat.isEmpty && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as! String != "1") && (dataMessages[indexPath.row]["lock"] as? String != "2")) {
  4509. topMarginText.constant = topMarginText.constant + 205
  4510. containerMessage.addSubview(imageThumb)
  4511. imageThumb.translatesAutoresizingMaskIntoConstraints = false
  4512. let data = queryMessageReply(message_id: reffChat)
  4513. if (data.count == 0) {
  4514. imageThumb.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 15).isActive = true
  4515. }
  4516. imageThumb.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
  4517. imageThumb.bottomAnchor.constraint(equalTo: messageText.topAnchor, constant: -5).isActive = true
  4518. imageThumb.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  4519. imageThumb.widthAnchor.constraint(equalToConstant: self.view.frame.size.width * 0.6).isActive = true
  4520. imageThumb.layer.cornerRadius = 5.0
  4521. imageThumb.clipsToBounds = true
  4522. imageThumb.contentMode = .scaleAspectFill
  4523. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  4524. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  4525. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  4526. if let dirPath = paths.first {
  4527. let thumbURL = URL(fileURLWithPath: dirPath).appendingPathComponent(thumbChat)
  4528. let image = UIImage(contentsOfFile: thumbURL.path)
  4529. imageThumb.image = image
  4530. let videoURL = URL(fileURLWithPath: dirPath).appendingPathComponent(videoChat)
  4531. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(imageChat)
  4532. if !FileManager.default.fileExists(atPath: imageURL.path) || !FileManager.default.fileExists(atPath: videoURL.path) {
  4533. let blurEffect = UIBlurEffect(style: UIBlurEffect.Style.light)
  4534. let blurEffectView = UIVisualEffectView(effect: blurEffect)
  4535. blurEffectView.frame = CGRect(x: 0, y: 0, width: imageThumb.frame.size.width, height: imageThumb.frame.size.height)
  4536. blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  4537. imageThumb.addSubview(blurEffectView)
  4538. if !imageChat.isEmpty {
  4539. let imageDownload = UIImageView(image: UIImage(systemName: "arrow.down.circle.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 50, weight: .bold, scale: .default)))
  4540. imageThumb.addSubview(imageDownload)
  4541. imageDownload.tintColor = .black.withAlphaComponent(0.3)
  4542. imageDownload.translatesAutoresizingMaskIntoConstraints = false
  4543. imageDownload.centerXAnchor.constraint(equalTo: imageThumb.centerXAnchor).isActive = true
  4544. imageDownload.centerYAnchor.constraint(equalTo: imageThumb.centerYAnchor).isActive = true
  4545. }
  4546. }
  4547. }
  4548. if (videoChat != "") {
  4549. 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))
  4550. imagePlay.circle()
  4551. imageThumb.addSubview(imagePlay)
  4552. imagePlay.backgroundColor = .black.withAlphaComponent(0.3)
  4553. imagePlay.translatesAutoresizingMaskIntoConstraints = false
  4554. imagePlay.centerXAnchor.constraint(equalTo: imageThumb.centerXAnchor).isActive = true
  4555. imagePlay.centerYAnchor.constraint(equalTo: imageThumb.centerYAnchor).isActive = true
  4556. }
  4557. if (dataMessages[indexPath.row]["progress"] as! Double != 100.0 && dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  4558. let container = UIView()
  4559. imageThumb.addSubview(container)
  4560. container.translatesAutoresizingMaskIntoConstraints = false
  4561. container.bottomAnchor.constraint(equalTo: imageThumb.bottomAnchor, constant: -10).isActive = true
  4562. container.leadingAnchor.constraint(equalTo: imageThumb.leadingAnchor, constant: 10).isActive = true
  4563. container.widthAnchor.constraint(equalToConstant: 30).isActive = true
  4564. container.heightAnchor.constraint(equalToConstant: 30).isActive = true
  4565. container.backgroundColor = .white.withAlphaComponent(0.1)
  4566. let circlePath = UIBezierPath(arcCenter: CGPoint(x: 10, y: 20), radius: 15, startAngle: -(.pi / 2), endAngle: .pi * 2, clockwise: true)
  4567. let trackShape = CAShapeLayer()
  4568. trackShape.path = circlePath.cgPath
  4569. trackShape.fillColor = UIColor.black.withAlphaComponent(0.3).cgColor
  4570. trackShape.lineWidth = 3
  4571. trackShape.strokeColor = UIColor.blueBubbleColor.withAlphaComponent(0.3).cgColor
  4572. container.backgroundColor = .clear
  4573. container.layer.addSublayer(trackShape)
  4574. let shapeLoading = CAShapeLayer()
  4575. shapeLoading.path = circlePath.cgPath
  4576. shapeLoading.fillColor = UIColor.clear.cgColor
  4577. shapeLoading.lineWidth = 3
  4578. shapeLoading.strokeEnd = 0
  4579. shapeLoading.strokeColor = UIColor.blueBubbleColor.cgColor
  4580. container.layer.addSublayer(shapeLoading)
  4581. let imageupload = UIImageView(image: UIImage(systemName: "arrow.up", withConfiguration: UIImage.SymbolConfiguration(pointSize: 10, weight: .bold, scale: .default)))
  4582. imageupload.tintColor = .white
  4583. container.addSubview(imageupload)
  4584. imageupload.translatesAutoresizingMaskIntoConstraints = false
  4585. imageupload.bottomAnchor.constraint(equalTo: imageThumb.bottomAnchor, constant: -10).isActive = true
  4586. imageupload.leadingAnchor.constraint(equalTo: imageThumb.leadingAnchor, constant: 10).isActive = true
  4587. imageupload.widthAnchor.constraint(equalToConstant: 20).isActive = true
  4588. imageupload.heightAnchor.constraint(equalToConstant: 20).isActive = true
  4589. }
  4590. if !copySession && !forwardSession && !deleteSession {
  4591. let objectTap = ObjectGesture(target: self, action: #selector(contentMessageTapped(_:)))
  4592. imageThumb.isUserInteractionEnabled = true
  4593. imageThumb.addGestureRecognizer(objectTap)
  4594. objectTap.image_id = imageChat
  4595. objectTap.video_id = videoChat
  4596. objectTap.imageView = imageThumb
  4597. objectTap.indexPath = indexPath
  4598. }
  4599. }
  4600. 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")) {
  4601. topMarginText.constant = topMarginText.constant + 55
  4602. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  4603. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  4604. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  4605. let arrExtFile = (textChat.components(separatedBy: "|")[0]).split(separator: ".")
  4606. let finalExtFile = arrExtFile[arrExtFile.count - 1]
  4607. if let dirPath = paths.first {
  4608. let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(fileChat)
  4609. if let dataFile = try? Data(contentsOf: fileURL) {
  4610. var sizeOfFile = Int(dataFile.count / 1000000)
  4611. if (sizeOfFile < 1) {
  4612. sizeOfFile = Int(dataFile.count / 1000)
  4613. if (finalExtFile.count > 4) {
  4614. messageText.text = "\(sizeOfFile) kB \u{2022} TXT"
  4615. }else {
  4616. messageText.text = "\(sizeOfFile) kB \u{2022} \(finalExtFile.uppercased())"
  4617. }
  4618. } else {
  4619. if (finalExtFile.count > 4) {
  4620. messageText.text = "\(sizeOfFile) MB \u{2022} TXT"
  4621. }else {
  4622. messageText.text = "\(sizeOfFile) MB \u{2022} \(finalExtFile.uppercased())"
  4623. }
  4624. }
  4625. } else {
  4626. messageText.text = ""
  4627. }
  4628. }
  4629. containerMessage.addSubview(containerViewFile)
  4630. containerViewFile.translatesAutoresizingMaskIntoConstraints = false
  4631. let data = queryMessageReply(message_id: reffChat)
  4632. if (data.count == 0) {
  4633. containerViewFile.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 15).isActive = true
  4634. }
  4635. containerViewFile.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
  4636. containerViewFile.bottomAnchor.constraint(equalTo:messageText.topAnchor, constant: -5).isActive = true
  4637. containerViewFile.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  4638. containerViewFile.heightAnchor.constraint(equalToConstant: 50).isActive = true
  4639. containerViewFile.backgroundColor = .black.withAlphaComponent(0.2)
  4640. containerViewFile.layer.cornerRadius = 5.0
  4641. containerViewFile.clipsToBounds = true
  4642. let imageFile = UIImageView(image: UIImage(systemName: "doc.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 30, weight: .bold, scale: .default)))
  4643. containerViewFile.addSubview(imageFile)
  4644. let nameFile = UILabel()
  4645. containerViewFile.addSubview(nameFile)
  4646. imageFile.translatesAutoresizingMaskIntoConstraints = false
  4647. imageFile.leadingAnchor.constraint(equalTo: containerViewFile.leadingAnchor, constant: 5).isActive = true
  4648. imageFile.trailingAnchor.constraint(equalTo: nameFile.leadingAnchor, constant: -5).isActive = true
  4649. imageFile.centerYAnchor.constraint(equalTo: containerViewFile.centerYAnchor).isActive = true
  4650. imageFile.widthAnchor.constraint(equalToConstant: 30).isActive = true
  4651. imageFile.heightAnchor.constraint(equalToConstant: 30).isActive = true
  4652. imageFile.tintColor = .docColor
  4653. nameFile.translatesAutoresizingMaskIntoConstraints = false
  4654. nameFile.centerYAnchor.constraint(equalTo: containerViewFile.centerYAnchor).isActive = true
  4655. nameFile.widthAnchor.constraint(lessThanOrEqualToConstant: 200).isActive = true
  4656. nameFile.font = UIFont.systemFont(ofSize: 12, weight: .medium)
  4657. nameFile.textColor = .white
  4658. nameFile.text = textChat.components(separatedBy: "|")[0]
  4659. if (dataMessages[indexPath.row]["progress"] as! Double != 100.0) {
  4660. let containerLoading = UIView()
  4661. containerViewFile.addSubview(containerLoading)
  4662. containerLoading.translatesAutoresizingMaskIntoConstraints = false
  4663. containerLoading.centerYAnchor.constraint(equalTo: containerViewFile.centerYAnchor).isActive = true
  4664. containerLoading.leadingAnchor.constraint(equalTo: nameFile.trailingAnchor, constant: 5).isActive = true
  4665. containerLoading.trailingAnchor.constraint(equalTo: containerViewFile.trailingAnchor, constant: -5).isActive = true
  4666. containerLoading.widthAnchor.constraint(equalToConstant: 30).isActive = true
  4667. containerLoading.heightAnchor.constraint(equalToConstant: 30).isActive = true
  4668. let circlePath = UIBezierPath(arcCenter: CGPoint(x: 15, y: 15), radius: 10, startAngle: -(.pi / 2), endAngle: .pi * 2, clockwise: true)
  4669. let trackShape = CAShapeLayer()
  4670. trackShape.path = circlePath.cgPath
  4671. trackShape.fillColor = UIColor.clear.cgColor
  4672. trackShape.lineWidth = 5
  4673. trackShape.strokeColor = UIColor.blueBubbleColor.withAlphaComponent(0.3).cgColor
  4674. containerLoading.layer.addSublayer(trackShape)
  4675. let shapeLoading = CAShapeLayer()
  4676. shapeLoading.path = circlePath.cgPath
  4677. shapeLoading.fillColor = UIColor.clear.cgColor
  4678. shapeLoading.lineWidth = 3
  4679. shapeLoading.strokeEnd = 0
  4680. shapeLoading.strokeColor = UIColor.secondaryColor.cgColor
  4681. containerLoading.layer.addSublayer(shapeLoading)
  4682. var imageupload = UIImageView(image: UIImage(systemName: "arrow.up", withConfiguration: UIImage.SymbolConfiguration(pointSize: 10, weight: .bold, scale: .default)))
  4683. if dataMessages[indexPath.row]["f_pin"] as? String != idMe {
  4684. imageupload = UIImageView(image: UIImage(systemName: "arrow.down", withConfiguration: UIImage.SymbolConfiguration(pointSize: 10, weight: .bold, scale: .default)))
  4685. shapeLoading.strokeColor = UIColor.blueBubbleColor.cgColor
  4686. }
  4687. imageupload.tintColor = .white
  4688. containerLoading.addSubview(imageupload)
  4689. imageupload.translatesAutoresizingMaskIntoConstraints = false
  4690. imageupload.centerYAnchor.constraint(equalTo: containerLoading.centerYAnchor).isActive = true
  4691. imageupload.centerXAnchor.constraint(equalTo: containerLoading.centerXAnchor).isActive = true
  4692. } else {
  4693. nameFile.trailingAnchor.constraint(equalTo: containerViewFile.trailingAnchor, constant: -5).isActive = true
  4694. }
  4695. if !copySession && !forwardSession && !deleteSession {
  4696. let objectTap = ObjectGesture(target: self, action: #selector(contentMessageTapped(_:)))
  4697. containerViewFile.addGestureRecognizer(objectTap)
  4698. objectTap.containerFile = containerViewFile
  4699. objectTap.labelFile = nameFile
  4700. objectTap.file_id = fileChat
  4701. objectTap.indexPath = indexPath
  4702. }
  4703. }
  4704. let containerLinkMessage = UIView()
  4705. if thumbChat.isEmpty && fileChat.isEmpty && !textChat.isEmpty {
  4706. var text = ""
  4707. let listTextSplitBreak = textChat.components(separatedBy: "\n")
  4708. let indexFirstLinkSplitBreak = listTextSplitBreak.firstIndex(where: { $0.contains("www.") || $0.contains("http://") || $0.contains("https://") })
  4709. if indexFirstLinkSplitBreak != nil {
  4710. let listTextSplitSpace = listTextSplitBreak[indexFirstLinkSplitBreak!].components(separatedBy: " ")
  4711. 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) })
  4712. if indexFirstLinkSplitSpace != nil {
  4713. text = listTextSplitSpace[indexFirstLinkSplitSpace!]
  4714. }
  4715. }
  4716. if !text.isEmpty {
  4717. var dataURL = ""
  4718. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  4719. if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "select data_link from LINK_PREVIEW where link='\(text)'"), cursor.next() {
  4720. if let data = cursor.string(forColumnIndex: 0) {
  4721. dataURL = data
  4722. }
  4723. cursor.close()
  4724. }
  4725. })
  4726. if !dataURL.isEmpty {
  4727. if let data = try! JSONSerialization.jsonObject(with: dataURL.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
  4728. let title = data["title"] as! String
  4729. let description = data["description"] as! String
  4730. let imageUrl = data["imageUrl"] as? String
  4731. let link = data["link"] as! String
  4732. topMarginText.constant = topMarginText.constant + 80
  4733. containerMessage.addSubview(containerLinkMessage)
  4734. containerLinkMessage.translatesAutoresizingMaskIntoConstraints = false
  4735. containerLinkMessage.leadingAnchor.constraint(equalTo:containerMessage.leadingAnchor, constant: 15).isActive = true
  4736. if dataMessages[indexPath.row]["attachment_flag"] as? String == "11" {
  4737. containerLinkMessage.bottomAnchor.constraint(equalTo: imageSticker.topAnchor, constant: -5).isActive = true
  4738. } else {
  4739. containerLinkMessage.bottomAnchor.constraint(equalTo: messageText.topAnchor, constant: -5).isActive = true
  4740. }
  4741. containerLinkMessage.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  4742. containerLinkMessage.heightAnchor.constraint(equalToConstant: 80.0).isActive = true
  4743. containerLinkMessage.backgroundColor = .gray.withAlphaComponent(0.2)
  4744. let imagePreview = UIImageView()
  4745. if imageUrl != nil {
  4746. containerLinkMessage.addSubview(imagePreview)
  4747. imagePreview.translatesAutoresizingMaskIntoConstraints = false
  4748. imagePreview.leadingAnchor.constraint(equalTo: containerLinkMessage.leadingAnchor).isActive = true
  4749. imagePreview.bottomAnchor.constraint(equalTo: containerLinkMessage.bottomAnchor).isActive = true
  4750. imagePreview.topAnchor.constraint(equalTo: containerLinkMessage.topAnchor).isActive = true
  4751. imagePreview.widthAnchor.constraint(equalToConstant: 80.0).isActive = true
  4752. if !imageUrl!.starts(with: "https://") {
  4753. imagePreview.loadImageAsync(with: "https://www.google.be" + imageUrl!)
  4754. } else {
  4755. imagePreview.loadImageAsync(with: imageUrl)
  4756. }
  4757. imagePreview.contentMode = .scaleAspectFit
  4758. }
  4759. let titlePreview = UILabel()
  4760. containerLinkMessage.addSubview(titlePreview)
  4761. titlePreview.translatesAutoresizingMaskIntoConstraints = false
  4762. if imageUrl != nil {
  4763. titlePreview.leadingAnchor.constraint(equalTo: imagePreview.trailingAnchor, constant: 5.0).isActive = true
  4764. } else {
  4765. titlePreview.leadingAnchor.constraint(equalTo: containerLinkMessage.leadingAnchor, constant: 5.0).isActive = true
  4766. }
  4767. titlePreview.topAnchor.constraint(equalTo: containerLinkMessage.topAnchor, constant: 25.0).isActive = true
  4768. titlePreview.trailingAnchor.constraint(equalTo: containerLinkMessage.trailingAnchor, constant: -80.0).isActive = true
  4769. titlePreview.text = title
  4770. titlePreview.font = UIFont.systemFont(ofSize: 14.0, weight: .bold)
  4771. titlePreview.textColor = .black
  4772. let descPreview = UILabel()
  4773. containerLinkMessage.addSubview(descPreview)
  4774. descPreview.translatesAutoresizingMaskIntoConstraints = false
  4775. if imageUrl != nil {
  4776. descPreview.leadingAnchor.constraint(equalTo: imagePreview.trailingAnchor, constant: 5.0).isActive = true
  4777. } else {
  4778. descPreview.leadingAnchor.constraint(equalTo: containerLinkMessage.leadingAnchor, constant: 5.0).isActive = true
  4779. }
  4780. descPreview.topAnchor.constraint(equalTo: titlePreview.bottomAnchor).isActive = true
  4781. descPreview.trailingAnchor.constraint(equalTo: containerLinkMessage.trailingAnchor, constant: -80.0).isActive = true
  4782. descPreview.text = description
  4783. descPreview.font = UIFont.systemFont(ofSize: 12.0)
  4784. descPreview.textColor = .gray
  4785. descPreview.numberOfLines = 1
  4786. let linkPreview = UILabel()
  4787. containerLinkMessage.addSubview(linkPreview)
  4788. linkPreview.translatesAutoresizingMaskIntoConstraints = false
  4789. if imageUrl != nil {
  4790. linkPreview.leadingAnchor.constraint(equalTo: imagePreview.trailingAnchor, constant: 5.0).isActive = true
  4791. } else {
  4792. linkPreview.leadingAnchor.constraint(equalTo: containerLinkMessage.leadingAnchor, constant: 5.0).isActive = true
  4793. }
  4794. linkPreview.topAnchor.constraint(equalTo: descPreview.bottomAnchor, constant: 8.0).isActive = true
  4795. linkPreview.trailingAnchor.constraint(equalTo: containerLinkMessage.trailingAnchor, constant: -80.0).isActive = true
  4796. linkPreview.text = link
  4797. linkPreview.font = UIFont.systemFont(ofSize: 10.0)
  4798. linkPreview.textColor = .gray
  4799. linkPreview.numberOfLines = 1
  4800. if !copySession && !forwardSession && !deleteSession {
  4801. let objectTap = ObjectGesture(target: self, action: #selector(tapMessageText(_:)))
  4802. objectTap.message_id = text
  4803. containerLinkMessage.addGestureRecognizer(objectTap)
  4804. }
  4805. }
  4806. }
  4807. }
  4808. }
  4809. if (reffChat != "" && dataMessages[indexPath.row]["message_scope_id"] as! String != "18") {
  4810. let data = queryMessageReply(message_id: reffChat)
  4811. if data.count != 0 {
  4812. topMarginText.constant = topMarginText.constant + 55
  4813. let containerReply = UIView()
  4814. containerMessage.addSubview(containerReply)
  4815. containerReply.translatesAutoresizingMaskIntoConstraints = false
  4816. containerReply.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
  4817. containerReply.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 15).isActive = true
  4818. if thumbChat != "" && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as! String != "1") {
  4819. containerReply.bottomAnchor.constraint(equalTo: imageThumb.topAnchor, constant: -5).isActive = true
  4820. } else if fileChat != "" && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as! String != "1") {
  4821. containerReply.bottomAnchor.constraint(equalTo: containerViewFile.topAnchor, constant: -5).isActive = true
  4822. } else if containerMessage.subviews.contains(containerLinkMessage) {
  4823. containerReply.bottomAnchor.constraint(equalTo: containerLinkMessage.topAnchor, constant: -5).isActive = true
  4824. } else if dataMessages[indexPath.row]["attachment_flag"] as? String == "11" && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as! String != "1") {
  4825. containerReply.bottomAnchor.constraint(equalTo: imageSticker.topAnchor, constant: -5).isActive = true
  4826. } else {
  4827. containerReply.bottomAnchor.constraint(equalTo: messageText.topAnchor, constant: -5).isActive = true
  4828. }
  4829. containerReply.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  4830. containerReply.heightAnchor.constraint(equalToConstant: 50).isActive = true
  4831. containerReply.backgroundColor = .black.withAlphaComponent(0.2)
  4832. containerReply.layer.cornerRadius = 5
  4833. containerReply.clipsToBounds = true
  4834. let leftReply = UIView()
  4835. containerReply.addSubview(leftReply)
  4836. leftReply.translatesAutoresizingMaskIntoConstraints = false
  4837. leftReply.leadingAnchor.constraint(equalTo: containerReply.leadingAnchor).isActive = true
  4838. leftReply.topAnchor.constraint(equalTo: containerReply.topAnchor).isActive = true
  4839. leftReply.bottomAnchor.constraint(equalTo: containerReply.bottomAnchor).isActive = true
  4840. leftReply.widthAnchor.constraint(equalToConstant: 3).isActive = true
  4841. leftReply.layer.cornerRadius = 5
  4842. leftReply.clipsToBounds = true
  4843. leftReply.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMinXMinYCorner]
  4844. let titleReply = UILabel()
  4845. containerReply.addSubview(titleReply)
  4846. titleReply.translatesAutoresizingMaskIntoConstraints = false
  4847. titleReply.leadingAnchor.constraint(equalTo: leftReply.leadingAnchor, constant: 10).isActive = true
  4848. titleReply.topAnchor.constraint(equalTo: containerReply.topAnchor, constant: 10).isActive = true
  4849. titleReply.trailingAnchor.constraint(lessThanOrEqualTo: containerReply.trailingAnchor, constant: -20).isActive = true
  4850. titleReply.font = UIFont.systemFont(ofSize: 12).bold
  4851. if (data["f_pin"] as? String == idMe) {
  4852. titleReply.text = "You".localized()
  4853. if dataMessages[indexPath.row]["f_pin"] as? String == idMe {
  4854. titleReply.textColor = .white
  4855. leftReply.backgroundColor = .white
  4856. } else {
  4857. titleReply.textColor = .mainColor
  4858. leftReply.backgroundColor = .mainColor
  4859. }
  4860. } else {
  4861. if isContactCenter {
  4862. let user: [User] = users.filter({$0.pin == data["f_pin"] as? String})
  4863. titleReply.text = user.first!.fullName
  4864. } else {
  4865. titleReply.text = self.dataPerson["name"]!!
  4866. }
  4867. if dataMessages[indexPath.row]["f_pin"] as? String == idMe {
  4868. titleReply.textColor = .white
  4869. leftReply.backgroundColor = .white
  4870. } else {
  4871. titleReply.textColor = .mainColor
  4872. leftReply.backgroundColor = .mainColor
  4873. }
  4874. }
  4875. let contentReply = UILabel()
  4876. containerReply.addSubview(contentReply)
  4877. contentReply.translatesAutoresizingMaskIntoConstraints = false
  4878. contentReply.leadingAnchor.constraint(equalTo: leftReply.leadingAnchor, constant: 10).isActive = true
  4879. contentReply.bottomAnchor.constraint(equalTo: containerReply.bottomAnchor, constant: -10).isActive = true
  4880. contentReply.font = UIFont.systemFont(ofSize: 10)
  4881. let message_text = data["message_text"] as! String
  4882. let attachment_flag = data["attachment_flag"] as! String
  4883. let thumb_chat = data["thumb_id"] as! String
  4884. let image_chat = data["image_id"] as! String
  4885. let video_chat = data["video_id"] as! String
  4886. let file_chat = data["file_id"] as! String
  4887. if (attachment_flag == "0" && thumb_chat == "") {
  4888. contentReply.trailingAnchor.constraint(equalTo: containerReply.trailingAnchor, constant: -20).isActive = true
  4889. contentReply.attributedText = message_text.richText()
  4890. } else if (attachment_flag == "1" || image_chat != "") {
  4891. if (message_text == "") {
  4892. contentReply.text = "📷 Photo".localized()
  4893. } else {
  4894. contentReply.attributedText = message_text.richText()
  4895. }
  4896. } else if (attachment_flag == "2" || video_chat != "") {
  4897. if (message_text == "") {
  4898. contentReply.text = "📹 Video".localized()
  4899. } else {
  4900. contentReply.attributedText = message_text.richText()
  4901. }
  4902. } else if (attachment_flag == "6" || file_chat != ""){
  4903. contentReply.trailingAnchor.constraint(equalTo: containerReply.trailingAnchor, constant: -20).isActive = true
  4904. contentReply.text = "📄 \(message_text.components(separatedBy: "|")[0])"
  4905. } else if (attachment_flag == "11") {
  4906. contentReply.text = "❤️ Sticker"
  4907. }
  4908. contentReply.textColor = .white.withAlphaComponent(0.8)
  4909. if (attachment_flag == "1" || attachment_flag == "2" || image_chat != "" || video_chat != "") {
  4910. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  4911. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  4912. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  4913. if let dirPath = paths.first {
  4914. let thumbURL = URL(fileURLWithPath: dirPath).appendingPathComponent(thumb_chat)
  4915. let image = UIImage(contentsOfFile: thumbURL.path)
  4916. let imageThumb = UIImageView(image: image)
  4917. containerReply.addSubview(imageThumb)
  4918. imageThumb.layer.cornerRadius = 2.0
  4919. imageThumb.clipsToBounds = true
  4920. imageThumb.contentMode = .scaleAspectFill
  4921. imageThumb.translatesAutoresizingMaskIntoConstraints = false
  4922. imageThumb.trailingAnchor.constraint(equalTo: containerReply.trailingAnchor, constant: -10).isActive = true
  4923. imageThumb.centerYAnchor.constraint(equalTo: containerReply.centerYAnchor).isActive = true
  4924. imageThumb.widthAnchor.constraint(equalToConstant: 30).isActive = true
  4925. imageThumb.heightAnchor.constraint(equalToConstant: 30).isActive = true
  4926. if (attachment_flag == "2") {
  4927. let imagePlay = UIImageView(image: UIImage(systemName: "play.circle.fill"))
  4928. imageThumb.addSubview(imagePlay)
  4929. imagePlay.clipsToBounds = true
  4930. imagePlay.translatesAutoresizingMaskIntoConstraints = false
  4931. imagePlay.centerYAnchor.constraint(equalTo: imageThumb.centerYAnchor).isActive = true
  4932. imagePlay.centerXAnchor.constraint(equalTo: imageThumb.centerXAnchor).isActive = true
  4933. imagePlay.widthAnchor.constraint(equalToConstant: 10).isActive = true
  4934. imagePlay.heightAnchor.constraint(equalToConstant: 10).isActive = true
  4935. imagePlay.tintColor = .white
  4936. }
  4937. titleReply.trailingAnchor.constraint(equalTo: imageThumb.leadingAnchor, constant: -20).isActive = true
  4938. contentReply.trailingAnchor.constraint(equalTo: imageThumb.leadingAnchor, constant: -20).isActive = true
  4939. }
  4940. }
  4941. if (attachment_flag == "11" && message_text.components(separatedBy: "/").count > 1) {
  4942. let imageSticker = UIImageView(image: UIImage(named: (message_text.components(separatedBy: "/")[1]), in: Bundle.resourceBundle(for: Nexilis.self), with: nil))
  4943. containerReply.addSubview(imageSticker)
  4944. imageSticker.layer.cornerRadius = 2.0
  4945. imageSticker.clipsToBounds = true
  4946. imageSticker.translatesAutoresizingMaskIntoConstraints = false
  4947. imageSticker.trailingAnchor.constraint(equalTo: containerReply.trailingAnchor, constant: -10).isActive = true
  4948. imageSticker.centerYAnchor.constraint(equalTo: containerReply.centerYAnchor).isActive = true
  4949. imageSticker.widthAnchor.constraint(equalToConstant: 30).isActive = true
  4950. imageSticker.heightAnchor.constraint(equalToConstant: 30).isActive = true
  4951. titleReply.trailingAnchor.constraint(equalTo: imageSticker.leadingAnchor, constant: -20).isActive = true
  4952. contentReply.trailingAnchor.constraint(equalTo: imageSticker.leadingAnchor, constant: -20).isActive = true
  4953. }
  4954. if !copySession && !forwardSession && !deleteSession {
  4955. let objectTap = ObjectGesture(target: self, action: #selector(contentMessageTapped(_:)))
  4956. containerReply.addGestureRecognizer(objectTap)
  4957. objectTap.indexPath = indexPath
  4958. objectTap.message_id = data["message_id"] as! String
  4959. }
  4960. }
  4961. }
  4962. // let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(panGestureCellAction))
  4963. // panGestureRecognizer.delegate = self
  4964. // cellMessage.addGestureRecognizer(panGestureRecognizer)
  4965. return cell
  4966. }
  4967. @objc func tapAck(_ sender: ObjectGesture) {
  4968. if blocking == "1" {
  4969. self.view.makeToast("You blocked this user".localized())
  4970. return
  4971. }
  4972. if blocking == "-1" {
  4973. self.view.makeToast("You have been blocked by this user".localized())
  4974. return
  4975. }
  4976. let indexPath = sender.indexPath
  4977. let dataMessages = self.dataMessages.filter({ $0["chat_date"] as! String == dataDates[indexPath.section]})
  4978. if dataMessages[indexPath.row]["status"] as! String == "8" {
  4979. return
  4980. }
  4981. if !CheckConnection.isConnectedToNetwork() || API.nGetCLXConnState() == 0 {
  4982. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  4983. imageView.tintColor = .white
  4984. 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)
  4985. banner.show()
  4986. return
  4987. }
  4988. DispatchQueue.global().async {
  4989. 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: "", latitude: "", description: ""))
  4990. if result != nil {
  4991. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  4992. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  4993. "status" : "8"
  4994. ], _where: "message_id = '\(dataMessages[indexPath.row]["message_id"] as! String)'")
  4995. })
  4996. DispatchQueue.main.async {
  4997. if let index = self.dataMessages.firstIndex(where: {$0["message_id"] as? String == dataMessages[indexPath.row]["message_id"] as? String}) {
  4998. self.dataMessages[index]["status"] = "8"
  4999. let section = self.dataDates.firstIndex(of: self.dataMessages[index]["chat_date"] as! String)
  5000. let row = self.dataMessages.filter({$0["chat_date"] as! String == self.dataMessages[index]["chat_date"] as! String}).firstIndex(where: { $0["message_id"] as? String == self.dataMessages[indexPath.row]["message_id"] as? String})
  5001. if row != nil && section != nil {
  5002. self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
  5003. }
  5004. self.showToast(message: "Confirmation Success.".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
  5005. }
  5006. }
  5007. }
  5008. }
  5009. }
  5010. // public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
  5011. // let velocity : CGPoint = gestureRecognizer.location(in: tableChatView)
  5012. // if velocity.x < 0 {
  5013. // return false
  5014. // }
  5015. // return abs(Float(velocity.x)) > abs(Float(velocity.y))
  5016. // }
  5017. //
  5018. // @objc func panGestureCellAction(recognizer: UIPanGestureRecognizer) {
  5019. // let translation = recognizer.translation(in: tableChatView)
  5020. // let x = recognizer.view?.frame.origin.x ?? 0
  5021. // if x >= -(recognizer.view?.frame.size.width ?? 0) * 0.05 {
  5022. // recognizer.view?.center = CGPoint(
  5023. // x: (recognizer.view?.center.x ?? 0) + translation.x,
  5024. // y: (recognizer.view?.center.y ?? 0))
  5025. // recognizer.setTranslation(CGPoint(x: 0, y: 0), in: view)
  5026. // if (recognizer.view?.frame.origin.x ?? 0) > UIScreen.main.bounds.size.width * 0.9 {
  5027. // UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseOut, animations: {
  5028. // 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)
  5029. // })
  5030. // }
  5031. // }
  5032. // if x <= -(recognizer.view?.frame.size.width ?? 0) * 0.05 {
  5033. // let idMe = UserDefaults.standard.string(forKey: "me") as String?
  5034. // let indexPath = self.tableChatView.indexPath(for: recognizer.view! as! UITableViewCell)
  5035. // let dataMessages = self.dataMessages.filter({ $0["chat_date"] as! String == dataDates[indexPath!.section]})
  5036. // if (dataMessages[indexPath!.row]["f_pin"] as? String == idMe) {
  5037. // let messageInfoVC = MessageInfo()
  5038. // messageInfoVC.data = dataMessages[indexPath!.row]
  5039. // self.navigationController?.pushViewController(messageInfoVC, animated: true)
  5040. // return
  5041. // }
  5042. // }
  5043. // if x >= ((recognizer.view?.frame.size.width ?? 0) * 0.2) {
  5044. // if !hapticSwipeLeft {
  5045. // UINotificationFeedbackGenerator().notificationOccurred(.success)
  5046. // }
  5047. // hapticSwipeLeft = true
  5048. // } else if x < ((recognizer.view?.frame.size.width ?? 0) * 0.2) {
  5049. // hapticSwipeLeft = false
  5050. // }
  5051. // if recognizer.state == .ended {
  5052. // UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseOut) {
  5053. // 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)
  5054. // } completion: { (finished) in
  5055. // if x > ((recognizer.view?.frame.size.width ?? 0) * 0.2) {
  5056. // self.hapticSwipeLeft = false
  5057. //
  5058. // }
  5059. // }
  5060. // }
  5061. // }
  5062. public func numberOfSections(in tableView: UITableView) -> Int {
  5063. dataDates.count
  5064. }
  5065. public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  5066. let count = dataMessages.filter({ $0["chat_date"] as! String == dataDates[section] }).count
  5067. return count
  5068. }
  5069. @objc func contentMessageTapped(_ sender: ObjectGesture) {
  5070. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  5071. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  5072. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  5073. if (sender.image_id != "") {
  5074. if let dirPath = paths.first {
  5075. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.image_id)
  5076. if FileManager.default.fileExists(atPath: imageURL.path) {
  5077. let image = UIImage(contentsOfFile: imageURL.path)
  5078. let previewImageVC = PreviewAttachmentImageVideo(nibName: "PreviewAttachmentImageVideo", bundle: Bundle.resourceBundle(for: Nexilis.self))
  5079. previewImageVC.image = image
  5080. previewImageVC.isHiddenTextField = true
  5081. previewImageVC.modalPresentationStyle = .custom
  5082. previewImageVC.modalTransitionStyle = .crossDissolve
  5083. self.present(previewImageVC, animated: true, completion: nil)
  5084. } else {
  5085. for view in sender.imageView.subviews {
  5086. if view is UIImageView {
  5087. view.removeFromSuperview()
  5088. }
  5089. }
  5090. let activityIndicator = UIActivityIndicatorView(style: .large)
  5091. activityIndicator.color = .mainColor
  5092. activityIndicator.hidesWhenStopped = true
  5093. activityIndicator.center = CGPoint(x:sender.imageView.frame.width/2,
  5094. y: sender.imageView.frame.height/2)
  5095. activityIndicator.startAnimating()
  5096. sender.imageView.addSubview(activityIndicator)
  5097. Download().start(forKey: sender.image_id) { (name, progress) in
  5098. guard progress == 100 else {
  5099. return
  5100. }
  5101. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.image_id)
  5102. let image = UIImage(contentsOfFile: imageURL.path)
  5103. let save = UserDefaults.standard.bool(forKey: "saveToGallery")
  5104. if save {
  5105. UIImageWriteToSavedPhotosAlbum(image!, nil, nil, nil)
  5106. }
  5107. DispatchQueue.main.async {
  5108. activityIndicator.stopAnimating()
  5109. self.tableChatView.reloadRows(at: [sender.indexPath], with: .none)
  5110. }
  5111. }
  5112. }
  5113. }
  5114. } else if (sender.video_id != "") {
  5115. if let dirPath = paths.first {
  5116. let videoURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.video_id)
  5117. if FileManager.default.fileExists(atPath: videoURL.path) {
  5118. let player = AVPlayer(url: videoURL as URL)
  5119. let playerVC = AVPlayerViewController()
  5120. playerVC.modalPresentationStyle = .custom
  5121. playerVC.player = player
  5122. self.present(playerVC, animated: true, completion: nil)
  5123. } else {
  5124. for view in sender.imageView.subviews {
  5125. if view is UIImageView {
  5126. view.removeFromSuperview()
  5127. }
  5128. }
  5129. let container = UIView()
  5130. sender.imageView.addSubview(container)
  5131. container.translatesAutoresizingMaskIntoConstraints = false
  5132. container.centerXAnchor.constraint(equalTo: sender.imageView.centerXAnchor).isActive = true
  5133. container.centerYAnchor.constraint(equalTo: sender.imageView.centerYAnchor).isActive = true
  5134. container.widthAnchor.constraint(equalToConstant: 50).isActive = true
  5135. container.heightAnchor.constraint(equalToConstant: 50).isActive = true
  5136. let circlePath = UIBezierPath(arcCenter: CGPoint(x: 25, y: 25), radius: 20, startAngle: -(.pi / 2), endAngle: .pi * 2, clockwise: true)
  5137. let trackShape = CAShapeLayer()
  5138. trackShape.path = circlePath.cgPath
  5139. trackShape.fillColor = UIColor.clear.cgColor
  5140. trackShape.lineWidth = 10
  5141. trackShape.strokeColor = UIColor.blueBubbleColor.withAlphaComponent(0.3).cgColor
  5142. container.backgroundColor = .clear
  5143. container.layer.addSublayer(trackShape)
  5144. let shapeLoading = CAShapeLayer()
  5145. shapeLoading.path = circlePath.cgPath
  5146. shapeLoading.fillColor = UIColor.clear.cgColor
  5147. shapeLoading.lineWidth = 10
  5148. shapeLoading.strokeEnd = 0
  5149. shapeLoading.strokeColor = UIColor.blueBubbleColor.cgColor
  5150. container.layer.addSublayer(shapeLoading)
  5151. let imageDownload = UIImageView(image: UIImage(systemName: "arrow.down", withConfiguration: UIImage.SymbolConfiguration(pointSize: 10, weight: .bold, scale: .default)))
  5152. imageDownload.tintColor = .white
  5153. container.addSubview(imageDownload)
  5154. imageDownload.translatesAutoresizingMaskIntoConstraints = false
  5155. imageDownload.centerXAnchor.constraint(equalTo: sender.imageView.centerXAnchor).isActive = true
  5156. imageDownload.centerYAnchor.constraint(equalTo: sender.imageView.centerYAnchor).isActive = true
  5157. imageDownload.widthAnchor.constraint(equalToConstant: 30).isActive = true
  5158. imageDownload.heightAnchor.constraint(equalToConstant: 30).isActive = true
  5159. Download().start(forKey: sender.video_id) { (name, progress) in
  5160. DispatchQueue.main.async {
  5161. guard progress == 100 else {
  5162. shapeLoading.strokeEnd = CGFloat(progress / 100)
  5163. return
  5164. }
  5165. let save = UserDefaults.standard.bool(forKey: "saveToGallery")
  5166. if save {
  5167. let videoURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.video_id)
  5168. PHPhotoLibrary.shared().performChanges({
  5169. PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: videoURL)
  5170. }) { saved, error in
  5171. }
  5172. }
  5173. let idx = self.dataMessages.firstIndex(where: { $0["video_id"] as! String == sender.video_id})
  5174. if idx != nil {
  5175. self.dataMessages[idx!]["progress"] = progress
  5176. self.tableChatView.reloadRows(at: [sender.indexPath], with: .none)
  5177. }
  5178. }
  5179. }
  5180. }
  5181. }
  5182. } else if (sender.file_id != "") {
  5183. if let dirPath = paths.first {
  5184. let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.file_id)
  5185. if FileManager.default.fileExists(atPath: fileURL.path) {
  5186. self.previewItem = fileURL as NSURL
  5187. let previewController = QLPreviewController()
  5188. let rightBarButton = UIBarButtonItem()
  5189. previewController.navigationItem.rightBarButtonItem = rightBarButton
  5190. previewController.dataSource = self
  5191. previewController.modalPresentationStyle = .custom
  5192. self.show(previewController, sender: nil)
  5193. } else {
  5194. for view in sender.containerFile.subviews {
  5195. if !(view is UIImageView) && !(view is UILabel) {
  5196. view.removeFromSuperview()
  5197. }
  5198. }
  5199. let containerLoading = UIView()
  5200. sender.containerFile.addSubview(containerLoading)
  5201. containerLoading.translatesAutoresizingMaskIntoConstraints = false
  5202. containerLoading.centerYAnchor.constraint(equalTo: sender.containerFile.centerYAnchor).isActive = true
  5203. containerLoading.leadingAnchor.constraint(equalTo: sender.labelFile.trailingAnchor, constant: 5).isActive = true
  5204. containerLoading.trailingAnchor.constraint(equalTo: sender.containerFile.trailingAnchor, constant: -5).isActive = true
  5205. containerLoading.widthAnchor.constraint(equalToConstant: 30).isActive = true
  5206. containerLoading.heightAnchor.constraint(equalToConstant: 30).isActive = true
  5207. let circlePath = UIBezierPath(arcCenter: CGPoint(x: 15, y: 15), radius: 10, startAngle: -(.pi / 2), endAngle: .pi * 2, clockwise: true)
  5208. let trackShape = CAShapeLayer()
  5209. trackShape.path = circlePath.cgPath
  5210. trackShape.fillColor = UIColor.clear.cgColor
  5211. trackShape.lineWidth = 5
  5212. trackShape.strokeColor = UIColor.blueBubbleColor.withAlphaComponent(0.3).cgColor
  5213. containerLoading.layer.addSublayer(trackShape)
  5214. let shapeLoading = CAShapeLayer()
  5215. shapeLoading.path = circlePath.cgPath
  5216. shapeLoading.fillColor = UIColor.clear.cgColor
  5217. shapeLoading.lineWidth = 3
  5218. shapeLoading.strokeEnd = 0
  5219. shapeLoading.strokeColor = UIColor.blueBubbleColor.cgColor
  5220. containerLoading.layer.addSublayer(shapeLoading)
  5221. let imageupload = UIImageView(image: UIImage(systemName: "arrow.down", withConfiguration: UIImage.SymbolConfiguration(pointSize: 10, weight: .bold, scale: .default)))
  5222. imageupload.tintColor = .white
  5223. containerLoading.addSubview(imageupload)
  5224. imageupload.translatesAutoresizingMaskIntoConstraints = false
  5225. imageupload.centerYAnchor.constraint(equalTo: containerLoading.centerYAnchor).isActive = true
  5226. imageupload.centerXAnchor.constraint(equalTo: containerLoading.centerXAnchor).isActive = true
  5227. Download().start(forKey: sender.file_id) { (name, progress) in
  5228. DispatchQueue.main.async {
  5229. guard progress == 100 else {
  5230. shapeLoading.strokeEnd = CGFloat(progress / 100)
  5231. return
  5232. }
  5233. let idx = self.dataMessages.firstIndex(where: { $0["file_id"] as! String == sender.file_id})
  5234. if idx != nil {
  5235. self.dataMessages[idx!]["progress"] = progress
  5236. self.tableChatView.reloadRows(at: [sender.indexPath], with: .none)
  5237. }
  5238. }
  5239. }
  5240. }
  5241. }
  5242. } else {
  5243. DispatchQueue.main.async {
  5244. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == sender.message_id})
  5245. if idx == nil {
  5246. return
  5247. }
  5248. let section = self.dataDates.firstIndex(of: self.dataMessages[idx!]["chat_date"] as! String)
  5249. if section == nil {
  5250. return
  5251. }
  5252. 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})
  5253. if row == nil {
  5254. return
  5255. }
  5256. let indexPath = IndexPath(row: row!, section: section!)
  5257. self.tableChatView.scrollToRow(at: indexPath, at: .middle, animated: true)
  5258. DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
  5259. if let cell = self.tableChatView.cellForRow(at: indexPath) {
  5260. let containerMessage = cell.contentView.subviews[0]
  5261. let idMe = UserDefaults.standard.string(forKey: "me") as String?
  5262. if (self.dataMessages[idx!]["f_pin"] as? String == idMe) {
  5263. containerMessage.backgroundColor = .blueBubbleColor.withAlphaComponent(0.3)
  5264. DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
  5265. if (self.dataMessages[idx!]["attachment_flag"] as? String == "11") {
  5266. containerMessage.backgroundColor = .clear
  5267. } else {
  5268. containerMessage.backgroundColor = .blueBubbleColor
  5269. }
  5270. }
  5271. } else {
  5272. containerMessage.backgroundColor = .grayColor.withAlphaComponent(0.3)
  5273. DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
  5274. if (self.dataMessages[idx!]["attachment_flag"] as? String == "11") {
  5275. containerMessage.backgroundColor = .clear
  5276. } else {
  5277. containerMessage.backgroundColor = .grayColor
  5278. }
  5279. }
  5280. }
  5281. }
  5282. }
  5283. }
  5284. }
  5285. }
  5286. @objc func handleLongPressLink(_ gestureRecognizer: UILongPressGestureRecognizer) {
  5287. func showMenuContext() {
  5288. if gestureRecognizer.state == .cancelled || gestureRecognizer.state == .ended{
  5289. timerCheckLink?.invalidate()
  5290. } else if gestureRecognizer.state == .began {
  5291. timerCheckLink = Timer.scheduledTimer(withTimeInterval: 0.2, repeats: false, block: {_ in
  5292. let interaction = UIContextMenuInteraction(delegate: self)
  5293. gestureRecognizer.view!.addInteraction(interaction)
  5294. guard let interaction = gestureRecognizer.view!.interactions.first,
  5295. let data = Data(base64Encoded: "X3ByZXNlbnRNZW51QXRMb2NhdGlvbjo="),
  5296. let str = String(data: data, encoding: .utf8)
  5297. else {
  5298. return
  5299. }
  5300. let selector = NSSelectorFromString(str)
  5301. guard interaction.responds(to: selector) else {
  5302. return
  5303. }
  5304. let impactHeavy = UIImpactFeedbackGenerator(style: .heavy)
  5305. impactHeavy.impactOccurred()
  5306. interaction.perform(selector, with: self.view)
  5307. self.isLinkCopied = true
  5308. })
  5309. }
  5310. }
  5311. if gestureRecognizer.state == .began {
  5312. let touchPoint = gestureRecognizer.location(in: self.view)
  5313. touchedSubview = self.view.hitTest(touchPoint, with: nil) ?? UIView()
  5314. if !(touchedSubview is UILabel) {
  5315. showMenuContext()
  5316. }
  5317. }
  5318. guard let label = touchedSubview as? UILabel else { return }
  5319. let touchPointLabel = gestureRecognizer.location(in: label)
  5320. if let text = label.text, let range = getWordRange(at: touchPointLabel, in: label) {
  5321. let word = String(text[range])
  5322. if word.starts(with: "www.") || word.starts(with: "https://") || word.starts(with: "http://") {
  5323. if gestureRecognizer.state == .cancelled || gestureRecognizer.state == .ended{
  5324. timerCheckLink?.invalidate()
  5325. if !isLinkCopied {
  5326. var stringURl = word
  5327. if stringURl.starts(with: "www.") {
  5328. stringURl = "https://" + stringURl.replacingOccurrences(of: "www.", with: "")
  5329. }
  5330. guard let url = URL(string: stringURl) else { return }
  5331. UIApplication.shared.open(url)
  5332. label.attributedText = removeHighlightedText(for: text, in: range, label: label)
  5333. }
  5334. isLinkCopied = false
  5335. } else if gestureRecognizer.state == .began {
  5336. label.attributedText = highlightedText(for: text, in: range, label: label)
  5337. timerCheckLink = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: {_ in
  5338. self.isLinkCopied = true
  5339. UIPasteboard.general.string = word
  5340. self.showToast(message: "Link Copied".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
  5341. label.attributedText = self.removeHighlightedText(for: text, in: range, label: label)
  5342. })
  5343. }
  5344. } else {
  5345. showMenuContext()
  5346. }
  5347. } else {
  5348. showMenuContext()
  5349. }
  5350. }
  5351. func getWordRange(at point: CGPoint, in label: UILabel) -> Range<String.Index>? {
  5352. guard let text = label.text else { return nil }
  5353. let layoutManager = NSLayoutManager()
  5354. let textContainer = NSTextContainer(size: label.frame.size)
  5355. let textStorage = NSTextStorage(attributedString: NSAttributedString(string: text))
  5356. layoutManager.addTextContainer(textContainer)
  5357. textStorage.addLayoutManager(layoutManager)
  5358. textContainer.lineFragmentPadding = 0
  5359. textContainer.maximumNumberOfLines = label.numberOfLines
  5360. lastTouchPoint = point
  5361. let characterIndex = layoutManager.characterIndex(for: point, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
  5362. if characterIndex == text.count - 1 {
  5363. return nil
  5364. }
  5365. var wordStartIndex = characterIndex
  5366. while wordStartIndex > 0 && text[text.index(text.startIndex, offsetBy: wordStartIndex - 1)] != " " && text[text.index(text.startIndex, offsetBy: wordStartIndex - 1)] != "\n" {
  5367. wordStartIndex -= 1
  5368. }
  5369. var wordEndIndex = characterIndex
  5370. while wordEndIndex < text.count && text[text.index(text.startIndex, offsetBy: wordEndIndex)] != " " && text[text.index(text.startIndex, offsetBy: wordEndIndex)] != "\n" {
  5371. wordEndIndex += 1
  5372. }
  5373. return text.index(text.startIndex, offsetBy: wordStartIndex)..<text.index(text.startIndex, offsetBy: wordEndIndex)
  5374. }
  5375. func highlightedText(for text: String, in range: Range<String.Index>, label: UILabel) -> NSAttributedString {
  5376. let mutableAttributedString = label.attributedText!.mutableCopy() as! NSMutableAttributedString
  5377. mutableAttributedString.addAttribute(.backgroundColor, value: UIColor.lightGray.withAlphaComponent(0.5), range: NSRange(range, in: text))
  5378. return mutableAttributedString
  5379. }
  5380. func removeHighlightedText(for text: String, in range: Range<String.Index>, label: UILabel) -> NSAttributedString {
  5381. let mutableAttributedString = label.attributedText!.mutableCopy() as! NSMutableAttributedString
  5382. mutableAttributedString.removeAttribute(.backgroundColor, range: NSRange(range, in: text))
  5383. return mutableAttributedString
  5384. }
  5385. @objc func tapMessageText(_ sender: ObjectGesture) {
  5386. var stringURl = sender.message_id.lowercased()
  5387. if stringURl.starts(with: "www.") {
  5388. stringURl = "https://" + stringURl.replacingOccurrences(of: "www.", with: "")
  5389. }
  5390. guard let url = URL(string: stringURl) else { return }
  5391. UIApplication.shared.open(url)
  5392. }
  5393. // public func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
  5394. // if copySession || forwardSession || deleteSession {
  5395. // return nil
  5396. // }
  5397. // let idMe = UserDefaults.standard.string(forKey: "me") as String?
  5398. // if (dataMessages[indexPath.row]["f_pin"] as? String != idMe) {
  5399. // return nil
  5400. // }
  5401. // let messageInfoVC = MessageInfo()
  5402. // messageInfoVC.data = dataMessages[indexPath.row]
  5403. // self.navigationController?.show(messageInfoVC, sender: nil)
  5404. // return UISwipeActionsConfiguration()
  5405. // }
  5406. //
  5407. // public func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
  5408. // if copySession || forwardSession || deleteSession {
  5409. // return nil
  5410. // }
  5411. // let action = UIContextualAction(style: .normal,
  5412. // title: "") { [weak self] (action, view, completionHandler) in
  5413. // self?.handleReply(indexPath: indexPath)
  5414. // completionHandler(true)
  5415. // }
  5416. // action.backgroundColor = .white
  5417. // action.image = UIImage(systemName: "arrowshape.turn.up.left.fill")?.withTintColor(.black, renderingMode: .alwaysOriginal)
  5418. // return UISwipeActionsConfiguration(actions: [action])
  5419. // }
  5420. private func handleReply(indexPath: IndexPath) {
  5421. let dataMessages = self.dataMessages.filter({ $0["chat_date"] as! String == dataDates[indexPath.section]})
  5422. self.deleteReplyView()
  5423. self.textFieldSend.becomeFirstResponder()
  5424. self.reffId = dataMessages[indexPath.row]["message_id"] as? String
  5425. UIView.animate(withDuration: 0.25, delay: 0.0, options: .curveEaseInOut, animations: {
  5426. self.constraintTopTextField.constant = self.constraintTopTextField.constant + 50
  5427. }, completion: nil)
  5428. if (self.currentIndexpath != nil) {
  5429. DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
  5430. self.tableChatView.scrollToRow(at: IndexPath(row: self.currentIndexpath!.row, section: self.currentIndexpath!.section), at: .none, animated: false)
  5431. }
  5432. } else {
  5433. self.tableChatView.scrollToBottom()
  5434. }
  5435. self.viewTextfield.addSubview(self.containerPreviewReply)
  5436. self.containerPreviewReply.translatesAutoresizingMaskIntoConstraints = false
  5437. self.containerPreviewReply.leadingAnchor.constraint(equalTo: self.viewTextfield.leadingAnchor).isActive = true
  5438. self.containerPreviewReply.topAnchor.constraint(equalTo: self.viewTextfield.topAnchor).isActive = true
  5439. if !self.containerLink.isDescendant(of: self.viewTextfield) {
  5440. self.bottomAnchorPreviewReply = self.containerPreviewReply.bottomAnchor.constraint(equalTo: self.textFieldSend.topAnchor)
  5441. } else {
  5442. self.bottomAnchorPreviewReply = self.containerPreviewReply.bottomAnchor.constraint(equalTo: self.containerLink.topAnchor)
  5443. }
  5444. self.bottomAnchorPreviewReply.isActive = true
  5445. self.containerPreviewReply.trailingAnchor.constraint(equalTo: self.viewTextfield.trailingAnchor).isActive = true
  5446. self.containerPreviewReply.backgroundColor = .secondaryColor
  5447. let leftReply = UIView()
  5448. self.containerPreviewReply.addSubview(leftReply)
  5449. leftReply.translatesAutoresizingMaskIntoConstraints = false
  5450. leftReply.leadingAnchor.constraint(equalTo: self.viewTextfield.leadingAnchor).isActive = true
  5451. leftReply.topAnchor.constraint(equalTo: self.containerPreviewReply.topAnchor).isActive = true
  5452. leftReply.bottomAnchor.constraint(equalTo: self.containerPreviewReply.bottomAnchor).isActive = true
  5453. leftReply.widthAnchor.constraint(equalToConstant: 3).isActive = true
  5454. leftReply.backgroundColor = .orangeColor
  5455. let titleReply = UILabel()
  5456. self.containerPreviewReply.addSubview(titleReply)
  5457. titleReply.translatesAutoresizingMaskIntoConstraints = false
  5458. titleReply.leadingAnchor.constraint(equalTo: leftReply.leadingAnchor, constant: 10).isActive = true
  5459. titleReply.topAnchor.constraint(equalTo: self.containerPreviewReply.topAnchor, constant: 10).isActive = true
  5460. titleReply.font = UIFont.systemFont(ofSize: 12).bold
  5461. let idMe = UserDefaults.standard.string(forKey: "me") as String?
  5462. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  5463. titleReply.text = "You".localized()
  5464. } else {
  5465. if self.isContactCenter {
  5466. let user: [User] = self.users.filter({$0.pin == dataMessages[indexPath.row]["f_pin"] as? String})
  5467. titleReply.text = user.first!.fullName
  5468. } else {
  5469. titleReply.text = self.dataPerson["name"]!!
  5470. }
  5471. }
  5472. titleReply.textColor = .orangeColor
  5473. let contentReply = UILabel()
  5474. self.containerPreviewReply.addSubview(contentReply)
  5475. contentReply.translatesAutoresizingMaskIntoConstraints = false
  5476. contentReply.leadingAnchor.constraint(equalTo: leftReply.leadingAnchor, constant: 10).isActive = true
  5477. contentReply.topAnchor.constraint(equalTo: titleReply.bottomAnchor).isActive = true
  5478. contentReply.font = UIFont.systemFont(ofSize: 10)
  5479. let message_text = dataMessages[indexPath.row]["message_text"] as! String
  5480. let attachment_flag = dataMessages[indexPath.row]["attachment_flag"] as! String
  5481. let thumb_chat = dataMessages[indexPath.row]["thumb_id"] as! String
  5482. let image_chat = dataMessages[indexPath.row]["image_id"] as! String
  5483. let video_chat = dataMessages[indexPath.row]["video_id"] as! String
  5484. let file_chat = dataMessages[indexPath.row]["file_id"] as! String
  5485. if (attachment_flag == "0" && thumb_chat == "") {
  5486. contentReply.attributedText = message_text.richText()
  5487. } else if (attachment_flag == "1" || image_chat != "") {
  5488. if (message_text == "") {
  5489. contentReply.text = "📷 Photo".localized()
  5490. } else {
  5491. contentReply.attributedText = message_text.richText()
  5492. }
  5493. } else if (attachment_flag == "2" || video_chat != "") {
  5494. if (message_text == "") {
  5495. contentReply.text = "📹 Video".localized()
  5496. } else {
  5497. contentReply.attributedText = message_text.richText()
  5498. }
  5499. } else if (attachment_flag == "6" || file_chat != ""){
  5500. contentReply.text = "📄 \(message_text.components(separatedBy: "|")[0])"
  5501. } else if (attachment_flag == "11") {
  5502. contentReply.text = "❤️ Sticker"
  5503. }
  5504. contentReply.textColor = .gray
  5505. let buttonCancelReply = UIButton(type: .custom)
  5506. self.containerPreviewReply.addSubview(buttonCancelReply)
  5507. buttonCancelReply.translatesAutoresizingMaskIntoConstraints = false
  5508. buttonCancelReply.trailingAnchor.constraint(equalTo: self.containerPreviewReply.trailingAnchor, constant: -10).isActive = true
  5509. buttonCancelReply.centerYAnchor.constraint(equalTo: self.containerPreviewReply.centerYAnchor).isActive = true
  5510. buttonCancelReply.setImage(UIImage(systemName: "xmark.circle" , withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .regular, scale: .default)), for: .normal)
  5511. buttonCancelReply.addTarget(nil, action: #selector(self.deleteReplyView), for: .touchUpInside)
  5512. buttonCancelReply.backgroundColor = .clear
  5513. buttonCancelReply.tintColor = .mainColor
  5514. if (attachment_flag == "1" || attachment_flag == "2" || image_chat != "" || video_chat != "") {
  5515. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  5516. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  5517. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  5518. if let dirPath = paths.first {
  5519. let thumbURL = URL(fileURLWithPath: dirPath).appendingPathComponent(thumb_chat)
  5520. let image = UIImage(contentsOfFile: thumbURL.path)
  5521. let imageThumb = UIImageView(image: image)
  5522. self.containerPreviewReply.addSubview(imageThumb)
  5523. imageThumb.layer.cornerRadius = 2.0
  5524. imageThumb.clipsToBounds = true
  5525. imageThumb.translatesAutoresizingMaskIntoConstraints = false
  5526. imageThumb.trailingAnchor.constraint(equalTo: buttonCancelReply.leadingAnchor, constant: -10).isActive = true
  5527. imageThumb.centerYAnchor.constraint(equalTo: self.containerPreviewReply.centerYAnchor).isActive = true
  5528. imageThumb.widthAnchor.constraint(equalToConstant: 30).isActive = true
  5529. imageThumb.heightAnchor.constraint(equalToConstant: 30).isActive = true
  5530. if (attachment_flag == "2") {
  5531. let imagePlay = UIImageView(image: UIImage(systemName: "play.circle.fill"))
  5532. imageThumb.addSubview(imagePlay)
  5533. imagePlay.clipsToBounds = true
  5534. imagePlay.translatesAutoresizingMaskIntoConstraints = false
  5535. imagePlay.centerYAnchor.constraint(equalTo: imageThumb.centerYAnchor).isActive = true
  5536. imagePlay.centerXAnchor.constraint(equalTo: imageThumb.centerXAnchor).isActive = true
  5537. imagePlay.widthAnchor.constraint(equalToConstant: 10).isActive = true
  5538. imagePlay.heightAnchor.constraint(equalToConstant: 10).isActive = true
  5539. imagePlay.tintColor = .white
  5540. }
  5541. }
  5542. }
  5543. if (attachment_flag == "11") {
  5544. let imageSticker = UIImageView(image: UIImage(named: (message_text.components(separatedBy: "/")[1]), in: Bundle.resourceBundle(for: Nexilis.self), with: nil))
  5545. self.containerPreviewReply.addSubview(imageSticker)
  5546. imageSticker.layer.cornerRadius = 2.0
  5547. imageSticker.clipsToBounds = true
  5548. imageSticker.translatesAutoresizingMaskIntoConstraints = false
  5549. imageSticker.trailingAnchor.constraint(equalTo: buttonCancelReply.leadingAnchor, constant: -10).isActive = true
  5550. imageSticker.centerYAnchor.constraint(equalTo: self.containerPreviewReply.centerYAnchor).isActive = true
  5551. imageSticker.widthAnchor.constraint(equalToConstant: 30).isActive = true
  5552. imageSticker.heightAnchor.constraint(equalToConstant: 30).isActive = true
  5553. }
  5554. }
  5555. func scrollToFirstSearchMessage(indexScroll: Int = 1) {
  5556. if textSearch.count < 2 {
  5557. return
  5558. }
  5559. var lastIndex = 0
  5560. let messageTextForSearch: [[String: Any?]] = self.dataMessages.reversed()
  5561. for idx in 0..<messageTextForSearch.count {
  5562. if (messageTextForSearch[idx]["message_text"] as! String).lowercased().contains(textSearch) {
  5563. lastIndex += 1
  5564. if lastIndex < indexScroll {
  5565. continue
  5566. }
  5567. lastScrollIdxSearch = lastIndex
  5568. let section = self.dataDates.firstIndex(of: messageTextForSearch[idx]["chat_date"] as! String)
  5569. if section == nil {
  5570. return
  5571. }
  5572. 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})
  5573. if row == nil {
  5574. return
  5575. }
  5576. let indexPath = IndexPath(row: row!, section: section!)
  5577. self.tableChatView.scrollToRow(at: indexPath, at: .middle, animated: true)
  5578. DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
  5579. if let cell = self.tableChatView.cellForRow(at: indexPath) {
  5580. let containerMessage = cell.contentView.subviews[0]
  5581. let idMe = UserDefaults.standard.string(forKey: "me") as String?
  5582. if (messageTextForSearch[idx]["f_pin"] as? String == idMe) {
  5583. containerMessage.backgroundColor = .blueBubbleColor.withAlphaComponent(0.3)
  5584. DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
  5585. if (messageTextForSearch[idx]["attachment_flag"] as? String == "11") {
  5586. containerMessage.backgroundColor = .clear
  5587. } else {
  5588. containerMessage.backgroundColor = .blueBubbleColor
  5589. }
  5590. }
  5591. } else {
  5592. containerMessage.backgroundColor = .grayColor.withAlphaComponent(0.3)
  5593. DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
  5594. if (messageTextForSearch[idx]["attachment_flag"] as? String == "11") {
  5595. containerMessage.backgroundColor = .clear
  5596. } else {
  5597. containerMessage.backgroundColor = .grayColor
  5598. }
  5599. }
  5600. }
  5601. }
  5602. }
  5603. titleSearchMatches.isHidden = false
  5604. if countMatchesSearch != 0 {
  5605. if countMatchesSearch > 1 {
  5606. titleSearchMatches.text = "\(lastScrollIdxSearch) " + "of".localized() + " \(countMatchesSearch) " + "matches".localized()
  5607. } else {
  5608. titleSearchMatches.text = "\(countMatchesSearch) " + "matches".localized()
  5609. }
  5610. } else {
  5611. titleSearchMatches.text = "Not found".localized()
  5612. }
  5613. if lastScrollIdxSearch == countMatchesSearch || countMatchesSearch == 0 {
  5614. buttonUp.isEnabled = false
  5615. buttonUp.tintColor = .gray
  5616. } else {
  5617. buttonUp.isEnabled = true
  5618. buttonUp.tintColor = .mainColor
  5619. }
  5620. if countMatchesSearch == 0 || lastScrollIdxSearch == 1 || countMatchesSearch == 1 {
  5621. buttonDown.isEnabled = false
  5622. buttonDown.tintColor = .gray
  5623. } else {
  5624. buttonDown.isEnabled = true
  5625. buttonDown.tintColor = .mainColor
  5626. }
  5627. break
  5628. }
  5629. }
  5630. }
  5631. public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
  5632. let indexPath = tableChatView.indexPathsForVisibleRows?.first
  5633. if indexPath != nil {
  5634. let headerRect = tableChatView.rectForHeader(inSection: indexPath!.section)
  5635. let isPinned = headerRect.origin.y <= scrollView.contentOffset.y
  5636. if listViewOnSection.count != 0 && listViewOnSection.count - 1 <= indexPath!.section && indexPath!.row > 0 {
  5637. let headerView = listViewOnSection[currentIndexpath!.section]
  5638. headerView.isHidden = true
  5639. }
  5640. }
  5641. }
  5642. public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
  5643. if !decelerate {
  5644. let indexPath = tableChatView.indexPathsForVisibleRows?.first
  5645. if indexPath != nil {
  5646. let headerRect = tableChatView.rectForHeader(inSection: indexPath!.section)
  5647. let isPinned = headerRect.origin.y <= scrollView.contentOffset.y
  5648. if listViewOnSection.count != 0 && listViewOnSection.count - 1 <= indexPath!.section && isPinned {
  5649. let headerView = listViewOnSection[currentIndexpath!.section]
  5650. headerView.isHidden = true
  5651. }
  5652. }
  5653. }
  5654. }
  5655. }
  5656. extension UITableView {
  5657. func scrollToBottom(isAnimated:Bool = true){
  5658. DispatchQueue.main.async {
  5659. if self.numberOfSections == 0 {
  5660. return
  5661. }
  5662. let indexPath = IndexPath(
  5663. row: self.numberOfRows(inSection: self.numberOfSections-1) - 1,
  5664. section: self.numberOfSections - 1)
  5665. if indexPath.row != -1 {
  5666. self.scrollToRow(at: indexPath, at: .bottom, animated: isAnimated)
  5667. }
  5668. }
  5669. }
  5670. func scrollToTop(isAnimated:Bool = true) {
  5671. DispatchQueue.main.async {
  5672. let indexPath = IndexPath(row: 0, section: 0)
  5673. if indexPath.row != -1 {
  5674. self.scrollToRow(at: indexPath, at: .top, animated: isAnimated)
  5675. }
  5676. }
  5677. }
  5678. }
  5679. extension UIImage {
  5680. func imageWithInsets(insets: UIEdgeInsets) -> UIImage? {
  5681. UIGraphicsBeginImageContextWithOptions(
  5682. CGSize(width: self.size.width + insets.left + insets.right,
  5683. height: self.size.height + insets.top + insets.bottom), false, self.scale)
  5684. let _ = UIGraphicsGetCurrentContext()
  5685. let origin = CGPoint(x: insets.left, y: insets.top)
  5686. self.draw(at: origin)
  5687. let imageWithInsets = UIGraphicsGetImageFromCurrentImageContext()
  5688. UIGraphicsEndImageContext()
  5689. return imageWithInsets
  5690. }
  5691. }
  5692. extension EditorPersonal: UISearchBarDelegate {
  5693. public func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
  5694. textSearch = searchText.trimmingCharacters(in: .whitespacesAndNewlines)
  5695. countMatchesSearch = 0
  5696. titleSearchMatches.isHidden = true
  5697. tableChatView.reloadData()
  5698. scrollToFirstSearchMessage()
  5699. }
  5700. }
  5701. public class ObjectGesture: UITapGestureRecognizer {
  5702. public var message_id = ""
  5703. public var image_id = ""
  5704. public var video_id = ""
  5705. public var file_id = ""
  5706. public var imageView = UIImageView()
  5707. public var containerFile = UIView()
  5708. public var labelFile = UILabel()
  5709. public var videoURL: NSURL?
  5710. public var indexPath = IndexPath()
  5711. }
  5712. class navigationQLPreviewDocument: UIBarButtonItem {
  5713. var navigation = UINavigationController()
  5714. }
  5715. class segmentedControllerObject: UISegmentedControl {
  5716. var navigation = UINavigationController()
  5717. }