EditorPersonal.swift 475 KB

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