EditorPersonal.swift 420 KB

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