EditorPersonal.swift 474 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738
  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. picker.dismiss(animated: true, completion: nil)
  3349. guard let result = results.first else { return }
  3350. if result.itemProvider.hasItemConformingToTypeIdentifier("com.compuserve.gif") {
  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. result.itemProvider.loadObject(ofClass: UIImage.self) { object, error in
  3374. if let image = object as? UIImage {
  3375. DispatchQueue.main.async {
  3376. let previewImageVC = PreviewAttachmentImageVideo(nibName: "PreviewAttachmentImageVideo", bundle: Bundle.resourceBundle(for: Nexilis.self))
  3377. if (self.textFieldSend.textColor != .lightGray) {
  3378. previewImageVC.currentTextTextField = self.textFieldSend.text
  3379. }
  3380. previewImageVC.fromCopy = true
  3381. previewImageVC.image = image
  3382. previewImageVC.modalPresentationStyle = .custom
  3383. previewImageVC.delegate = self
  3384. previewImageVC.isAck = self.isAck
  3385. previewImageVC.isConfidential = self.isConfidential
  3386. previewImageVC.isCC = self.isContactCenter
  3387. self.present(previewImageVC, animated: true, completion: nil)
  3388. }
  3389. }
  3390. }
  3391. } else if result.itemProvider.hasItemConformingToTypeIdentifier("public.movie") {
  3392. result.itemProvider.loadFileRepresentation(forTypeIdentifier: "public.movie") { tempURL, error in
  3393. if let tempURL = tempURL {
  3394. let fileManager = FileManager.default
  3395. let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
  3396. let destinationURL = documentsDirectory.appendingPathComponent(tempURL.lastPathComponent)
  3397. do {
  3398. if fileManager.fileExists(atPath: destinationURL.path) {
  3399. try fileManager.removeItem(at: destinationURL)
  3400. }
  3401. try fileManager.copyItem(at: tempURL, to: destinationURL)
  3402. DispatchQueue.main.async {
  3403. let previewImageVC = PreviewAttachmentImageVideo(nibName: "PreviewAttachmentImageVideo", bundle: Bundle.resourceBundle(for: Nexilis.self))
  3404. if (self.textFieldSend.textColor != .lightGray) {
  3405. previewImageVC.currentTextTextField = self.textFieldSend.text
  3406. }
  3407. previewImageVC.modalPresentationStyle = .custom
  3408. previewImageVC.urlVideoPhpPicker = destinationURL
  3409. previewImageVC.delegate = self
  3410. previewImageVC.isAck = self.isAck
  3411. previewImageVC.isConfidential = self.isConfidential
  3412. previewImageVC.isCC = self.isContactCenter
  3413. self.present(previewImageVC, animated: true, completion: nil)
  3414. }
  3415. } catch {
  3416. print("Error copying video file: \(error.localizedDescription)")
  3417. }
  3418. }
  3419. }
  3420. }
  3421. }
  3422. func sendChatFromPreviewImage(message_text: String, attachment_flag: String, image_id: String, video_id: String, thumb_id: String, gif_id: String, viewController: UIViewController) {
  3423. 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)
  3424. }
  3425. }
  3426. //EQL
  3427. extension EditorPersonal: UIDocumentPickerDelegate, DocumentPickerDelegate, QLPreviewControllerDataSource {
  3428. public func didSelectDocument(document: Any?) {
  3429. if (document != nil) {
  3430. self.previewItem = (document as! [URL])[0] as NSURL
  3431. let previewController = QLPreviewController()
  3432. let navController = CustomNavigationController(rootViewController: previewController)
  3433. navController.navigationBar.tintColor = .white
  3434. navController.navigationBar.barTintColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : .mainColor
  3435. navController.navigationBar.isTranslucent = false
  3436. navController.navigationBar.overrideUserInterfaceStyle = .dark
  3437. navController.navigationBar.barStyle = .black
  3438. let cancelButtonAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.font : UIFont.systemFont(ofSize: 16)]
  3439. UIBarButtonItem.appearance().setTitleTextAttributes(cancelButtonAttributes, for: .normal)
  3440. let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
  3441. navController.navigationBar.titleTextAttributes = textAttributes
  3442. let leftBarButton = navigationQLPreviewDocument(title: "Cancel".localized(), style: .plain, target: self, action: #selector(cancelDocumentPreview))
  3443. let rightBarButton = navigationQLPreviewDocument(title: "Send".localized(), style: .done, target: self, action: #selector(sendDocument))
  3444. // leftBarButton.tintColor = .white
  3445. // rightBarButton.tintColor = .white
  3446. leftBarButton.navigation = navController
  3447. rightBarButton.navigation = navController
  3448. // navController.navigationBar.barTintColor = .mainColor
  3449. navController.navigationBar.isTranslucent = false
  3450. previewController.navigationItem.leftBarButtonItem = leftBarButton
  3451. previewController.navigationItem.rightBarButtonItem = rightBarButton
  3452. previewController.dataSource = self
  3453. previewController.modalPresentationStyle = .pageSheet
  3454. self.present(navController, animated: true, completion: nil)
  3455. }
  3456. }
  3457. @objc private func cancelDocumentPreview(sender: navigationQLPreviewDocument) {
  3458. sender.navigation.dismiss(animated: true, completion: nil)
  3459. }
  3460. @objc private func sendDocument(sender: navigationQLPreviewDocument) {
  3461. sender.navigation.dismiss(animated: true, completion: nil)
  3462. do {
  3463. let dataFile = try Data(contentsOf: self.previewItem! as URL)
  3464. let urlFile = self.previewItem?.absoluteString
  3465. var originaFileName = (urlFile! as NSString).lastPathComponent
  3466. originaFileName = NSString(string: originaFileName).removingPercentEncoding!
  3467. let renamedNameFile = "Nexilis_doc_" + "\(Date().currentTimeMillis())_" + originaFileName
  3468. let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
  3469. let fileURL = documentsDirectory.appendingPathComponent(renamedNameFile)
  3470. if !FileManager.default.fileExists(atPath: fileURL.path) {
  3471. do {
  3472. try dataFile.write(to: fileURL)
  3473. //print("file saved")
  3474. } catch {
  3475. //print("error saving file:", error)
  3476. }
  3477. }
  3478. sendChat(message_text: "\(originaFileName)|", attachment_flag: "6", file_id: renamedNameFile, viewController: self)
  3479. } catch {
  3480. }
  3481. }
  3482. public func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
  3483. return self.previewItem != nil ? 1 : 0
  3484. }
  3485. public func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
  3486. return self.previewItem!
  3487. }
  3488. }
  3489. //ETV
  3490. extension EditorPersonal: UITextViewDelegate {
  3491. public func textViewDidChangeSelection(_ textView: UITextView) {
  3492. var nowTextFieldSend = self.textFieldSend
  3493. if isEditingMessage {
  3494. nowTextFieldSend = editTextView
  3495. }
  3496. let cursorPosition = textView.caretRect(for: nowTextFieldSend!.selectedTextRange!.start).origin
  3497. let doubleCurrentLine = cursorPosition.y / nowTextFieldSend!.font!.lineHeight
  3498. if doubleCurrentLine.isFinite {
  3499. let currentLine = Int(doubleCurrentLine)
  3500. UIView.animate(withDuration: 0.3) {
  3501. let numberOfLines = textView.textContainer.lineBreakMode == .byWordWrapping ? Int(textView.contentSize.height / textView.font!.lineHeight) - 1 : 1
  3502. if currentLine == 0 && numberOfLines == 1 {
  3503. self.heightTextFieldSend.constant = 40
  3504. if self.isEditingMessage {
  3505. self.constraintHeighteditTextView.constant = 40
  3506. }
  3507. } else if (self.heightTextFieldSend.constant < 95.0 || (self.constraintHeighteditTextView != nil && self.constraintHeighteditTextView.constant < 95.0)) && currentLine >= 4 {
  3508. self.heightTextFieldSend.constant = 95.0
  3509. if self.isEditingMessage {
  3510. self.constraintHeighteditTextView.constant = 95.0
  3511. }
  3512. } else if currentLine < 4 && numberOfLines < 5 {
  3513. if (nowTextFieldSend!.text.count > 0 && self.heightTextFieldSend.constant != nowTextFieldSend!.contentSize.height) {
  3514. self.heightTextFieldSend.constant = nowTextFieldSend!.contentSize.height
  3515. if self.isEditingMessage {
  3516. self.constraintHeighteditTextView.constant = nowTextFieldSend!.contentSize.height
  3517. }
  3518. }
  3519. }
  3520. }
  3521. }
  3522. }
  3523. public func textViewDidChange(_ textView: UITextView) {
  3524. if textView.text.count == 0 {
  3525. isAlwaysHideLinkPreview = false
  3526. }
  3527. if allowTyping {
  3528. allowTyping = false
  3529. if isContactCenter && !fPinContacCenter.isEmpty {
  3530. sendTyping(l_pin: fPinContacCenter, isTyping: true)
  3531. } else {
  3532. sendTyping(l_pin: dataPerson["f_pin"]!!, isTyping: true)
  3533. }
  3534. DispatchQueue.main.asyncAfter(deadline: .now() + 4, execute: {
  3535. self.allowTyping = true
  3536. })
  3537. }
  3538. timerCheckLink?.invalidate()
  3539. timerCheckLink = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: {_ in
  3540. self.checkLink(fullText: textView.text)
  3541. })
  3542. if textView.text.contains("*") || textView.text.contains("_") || textView.text.contains("^") || textView.text.contains("~") {
  3543. textView.preserveCursorPosition(withChanges: { _ in
  3544. textView.attributedText = textView.text.richText(isEditing: true)
  3545. return .preserveCursor
  3546. })
  3547. }
  3548. }
  3549. private func checkLink(fullText: String) {
  3550. if !isAlwaysHideLinkPreview {
  3551. var text = ""
  3552. let listTextSplitBreak = fullText.components(separatedBy: "\n")
  3553. let indexFirstLinkSplitBreak = listTextSplitBreak.firstIndex(where: { $0.contains("www.") || $0.contains("http://") || $0.contains("https://") })
  3554. if indexFirstLinkSplitBreak != nil {
  3555. let listTextSplitSpace = listTextSplitBreak[indexFirstLinkSplitBreak!].components(separatedBy: " ")
  3556. 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) })
  3557. if indexFirstLinkSplitSpace != nil {
  3558. text = listTextSplitSpace[indexFirstLinkSplitSpace!]
  3559. }
  3560. }
  3561. if !text.isEmpty {
  3562. var stringURl = text
  3563. if stringURl.starts(with: "www.") {
  3564. stringURl = "https://" + stringURl.replacingOccurrences(of: "www.", with: "")
  3565. }
  3566. var dataURL = ""
  3567. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  3568. do {
  3569. if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "select data_link from LINK_PREVIEW where link='\(text)'") {
  3570. while cursor.next() {
  3571. if let data = cursor.string(forColumnIndex: 0) {
  3572. dataURL = data
  3573. }
  3574. }
  3575. cursor.close()
  3576. }
  3577. } catch {
  3578. rollback.pointee = true
  3579. print("Access database error: \(error.localizedDescription)")
  3580. }
  3581. })
  3582. if !dataURL.isEmpty {
  3583. if let data = try! JSONSerialization.jsonObject(with: dataURL.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
  3584. let title = data["title"] as? String ?? ""
  3585. let description = data["description"] as? String ?? ""
  3586. let imageUrl = data["imageUrl"] as? String
  3587. let link = data["link"] as? String ?? ""
  3588. if self.showingLink != text {
  3589. self.showingLink = text
  3590. self.deleteLinkPreview()
  3591. self.buildPreviewLink(imageUrl: imageUrl, title: title, description: description, stringURl: link)
  3592. }
  3593. }
  3594. } else {
  3595. let urlConfig = URLSessionConfiguration.default
  3596. let sessionDelegate = SelfSignedURLSessionDelegate()
  3597. let session = URLSession(configuration: urlConfig, delegate: sessionDelegate, delegateQueue: nil)
  3598. let slp = SwiftLinkPreview(session: session,
  3599. workQueue: SwiftLinkPreview.defaultWorkQueue,
  3600. responseQueue: DispatchQueue.main,
  3601. cache: DisabledCache.instance)
  3602. let preview = slp.preview(stringURl,
  3603. onSuccess: { result in
  3604. let title = result.title ?? "No Title"
  3605. let description = stringURl.contains("google.com") ? "" : result.description
  3606. let imageUrl = result.icon
  3607. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  3608. do {
  3609. var dataJson: [String: Any] = [:]
  3610. dataJson["title"] = title
  3611. dataJson["description"] = description
  3612. dataJson["imageUrl"] = imageUrl
  3613. dataJson["link"] = text
  3614. guard let json = String(data: try! JSONSerialization.data(withJSONObject: dataJson, options: []), encoding: String.Encoding.utf8) else {
  3615. return
  3616. }
  3617. _ = try Database.shared.insertRecord(fmdb: fmdb, table: "LINK_PREVIEW", cvalues: [
  3618. "id" : "\(Date().currentTimeMillis().toHex())",
  3619. "link" : text,
  3620. "data_link" : json,
  3621. "retry": 0
  3622. ], replace: true)
  3623. } catch {
  3624. rollback.pointee = true
  3625. print("Access database error: \(error.localizedDescription)")
  3626. }
  3627. })
  3628. if self.showingLink != text {
  3629. self.showingLink = text
  3630. self.deleteLinkPreview()
  3631. self.buildPreviewLink(imageUrl: imageUrl, title: title, description: description, stringURl: text)
  3632. }
  3633. },
  3634. onError: { error in
  3635. self.deleteLinkPreview()
  3636. })
  3637. }
  3638. } else {
  3639. deleteLinkPreview()
  3640. }
  3641. }
  3642. }
  3643. private func buildPreviewLink(imageUrl: String?, title: String, description: String?, stringURl: String) {
  3644. if !self.viewTextfield.subviews.contains(self.containerLink){
  3645. UIView.animate(withDuration: 0.25, delay: 0.0, options: .curveEaseInOut, animations: {
  3646. self.constraintTopTextField.constant = self.constraintTopTextField.constant + 80
  3647. }, completion: nil)
  3648. }
  3649. self.viewTextfield.addSubview(self.containerLink)
  3650. self.containerLink.translatesAutoresizingMaskIntoConstraints = false
  3651. self.containerLink.leadingAnchor.constraint(equalTo: self.viewTextfield.leadingAnchor).isActive = true
  3652. self.containerLink.bottomAnchor.constraint(equalTo: self.textFieldSend.topAnchor).isActive = true
  3653. self.containerLink.trailingAnchor.constraint(equalTo: self.viewTextfield.trailingAnchor).isActive = true
  3654. self.containerLink.heightAnchor.constraint(equalToConstant: 80.0).isActive = true
  3655. self.containerLink.backgroundColor = .secondaryColor
  3656. if self.reffId != nil {
  3657. self.bottomAnchorPreviewReply.isActive = false
  3658. self.bottomAnchorPreviewReply = self.containerPreviewReply.bottomAnchor.constraint(equalTo: self.containerLink.topAnchor)
  3659. self.bottomAnchorPreviewReply.isActive = true
  3660. }
  3661. let imagePreview = UIImageView()
  3662. if imageUrl != nil {
  3663. self.containerLink.addSubview(imagePreview)
  3664. imagePreview.translatesAutoresizingMaskIntoConstraints = false
  3665. imagePreview.leadingAnchor.constraint(equalTo: self.containerLink.leadingAnchor).isActive = true
  3666. imagePreview.bottomAnchor.constraint(equalTo: self.containerLink.bottomAnchor).isActive = true
  3667. imagePreview.topAnchor.constraint(equalTo: self.containerLink.topAnchor).isActive = true
  3668. imagePreview.widthAnchor.constraint(equalToConstant: 80.0).isActive = true
  3669. imagePreview.loadImageAsync(with: imageUrl)
  3670. imagePreview.contentMode = .scaleAspectFit
  3671. }
  3672. let titlePreview = UILabel()
  3673. self.containerLink.addSubview(titlePreview)
  3674. titlePreview.translatesAutoresizingMaskIntoConstraints = false
  3675. if imageUrl != nil {
  3676. titlePreview.leadingAnchor.constraint(equalTo: imagePreview.trailingAnchor, constant: 5.0).isActive = true
  3677. } else {
  3678. titlePreview.leadingAnchor.constraint(equalTo: self.containerLink.leadingAnchor, constant: 5.0).isActive = true
  3679. }
  3680. titlePreview.topAnchor.constraint(equalTo: self.containerLink.topAnchor, constant: 25.0).isActive = true
  3681. titlePreview.trailingAnchor.constraint(equalTo: self.containerLink.trailingAnchor, constant: -80.0).isActive = true
  3682. titlePreview.text = title
  3683. titlePreview.font = UIFont.systemFont(ofSize: 14.0, weight: .bold)
  3684. titlePreview.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
  3685. let descPreview = UILabel()
  3686. self.containerLink.addSubview(descPreview)
  3687. descPreview.translatesAutoresizingMaskIntoConstraints = false
  3688. if imageUrl != nil {
  3689. descPreview.leadingAnchor.constraint(equalTo: imagePreview.trailingAnchor, constant: 5.0).isActive = true
  3690. } else {
  3691. descPreview.leadingAnchor.constraint(equalTo: self.containerLink.leadingAnchor, constant: 5.0).isActive = true
  3692. }
  3693. descPreview.topAnchor.constraint(equalTo: titlePreview.bottomAnchor).isActive = true
  3694. descPreview.trailingAnchor.constraint(equalTo: self.containerLink.trailingAnchor, constant: -80.0).isActive = true
  3695. descPreview.text = description
  3696. descPreview.font = UIFont.systemFont(ofSize: 12.0)
  3697. descPreview.textColor = .gray
  3698. descPreview.numberOfLines = 1
  3699. let linkPreview = UILabel()
  3700. self.containerLink.addSubview(linkPreview)
  3701. linkPreview.translatesAutoresizingMaskIntoConstraints = false
  3702. if imageUrl != nil {
  3703. linkPreview.leadingAnchor.constraint(equalTo: imagePreview.trailingAnchor, constant: 5.0).isActive = true
  3704. } else {
  3705. linkPreview.leadingAnchor.constraint(equalTo: self.containerLink.leadingAnchor, constant: 5.0).isActive = true
  3706. }
  3707. linkPreview.topAnchor.constraint(equalTo: descPreview.bottomAnchor, constant: 8.0).isActive = true
  3708. linkPreview.trailingAnchor.constraint(equalTo: self.containerLink.trailingAnchor, constant: -80.0).isActive = true
  3709. linkPreview.text = stringURl
  3710. linkPreview.font = UIFont.systemFont(ofSize: 10.0)
  3711. linkPreview.textColor = .gray
  3712. linkPreview.numberOfLines = 1
  3713. let cancelPreview = UIButton(type: .custom)
  3714. self.containerLink.addSubview(cancelPreview)
  3715. cancelPreview.translatesAutoresizingMaskIntoConstraints = false
  3716. cancelPreview.trailingAnchor.constraint(equalTo: self.containerLink.trailingAnchor, constant: -10).isActive = true
  3717. cancelPreview.centerYAnchor.constraint(equalTo: self.containerLink.centerYAnchor).isActive = true
  3718. cancelPreview.setImage(UIImage(systemName: "xmark.circle" , withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .regular, scale: .default)), for: .normal)
  3719. cancelPreview.addTarget(nil, action: #selector(self.removeLinkPreviewUntilEmptyTextView), for: .touchUpInside)
  3720. cancelPreview.backgroundColor = .clear
  3721. cancelPreview.tintColor = .mainColor
  3722. }
  3723. public func textViewDidBeginEditing(_ textView: UITextView) {
  3724. if textView.textColor == UIColor.lightGray {
  3725. textView.text = nil
  3726. textView.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : UIColor.black
  3727. }
  3728. }
  3729. public func textViewDidEndEditing(_ textView: UITextView) {
  3730. if textView.text.isEmpty {
  3731. textView.text = "Send message".localized()
  3732. textView.textColor = UIColor.lightGray
  3733. }
  3734. }
  3735. public func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
  3736. if let pasteboardItems = UIPasteboard.general.items.first {
  3737. if pasteboardItems["public.jpeg"] != nil || pasteboardItems["public.png"] != nil || pasteboardItems["public.gif"] != nil || (pasteboardItems.keys.first != nil && pasteboardItems.keys.first!.contains(".gif")) {
  3738. let dataGif = UIPasteboard.general.data(forPasteboardType: "com.compuserve.gif")
  3739. let previewImageVC = PreviewAttachmentImageVideo(nibName: "PreviewAttachmentImageVideo", bundle: Bundle.resourceBundle(for: Nexilis.self))
  3740. previewImageVC.image = pasteboardItems["public.png"] as? UIImage ?? pasteboardItems["public.jpeg"] as? UIImage
  3741. previewImageVC.isGIF = (pasteboardItems["public.png"] == nil && pasteboardItems["public.jpeg"] == nil)
  3742. previewImageVC.fromCopy = true
  3743. previewImageVC.dataGIF = dataGif
  3744. previewImageVC.currentTextTextField = textFieldSend.text
  3745. previewImageVC.modalPresentationStyle = .custom
  3746. previewImageVC.delegate = self
  3747. previewImageVC.isAck = self.isAck
  3748. previewImageVC.isConfidential = self.isConfidential
  3749. previewImageVC.isCC = self.isContactCenter
  3750. self.present(previewImageVC, animated: true, completion: nil)
  3751. return false
  3752. }
  3753. }
  3754. if (self.textFieldSend.text.count == 0) {
  3755. return text != "\n"
  3756. }
  3757. return true
  3758. }
  3759. func isGIFData(_ data: Data) -> Bool {
  3760. let gifSignature: [UInt8] = [0x47, 0x49, 0x46, 0x38, 0x37, 0x61]
  3761. let rawData = [UInt8](data.prefix(6))
  3762. return rawData == gifSignature
  3763. }
  3764. public func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
  3765. switch interaction {
  3766. case .invokeDefaultAction:
  3767. let gesture = ObjectGesture()
  3768. gesture.message_id = URL.absoluteString
  3769. tapMessageText(gesture)
  3770. return false
  3771. case .presentActions:
  3772. UIPasteboard.general.string = URL.absoluteString
  3773. self.view.makeToast("Link Copied".localized(), duration: 3)
  3774. return false
  3775. case .preview:
  3776. return true
  3777. @unknown default:
  3778. return true
  3779. }
  3780. }
  3781. }
  3782. //EUC
  3783. extension EditorPersonal: UIContextMenuInteractionDelegate {
  3784. public func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willEndFor configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionAnimating?) {
  3785. if showMenuContext {
  3786. showMenuContext = false
  3787. interaction.view!.removeInteraction(interaction)
  3788. }
  3789. }
  3790. public func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
  3791. if textFieldSend.isFirstResponder {
  3792. textFieldSend.resignFirstResponder()
  3793. }
  3794. let indexPath = self.tableChatView.indexPathForRow(at: interaction.view!.convert(location, to: self.tableChatView))
  3795. let dataMessages = self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == dataDates[indexPath!.section]})
  3796. var star: UIAction
  3797. if (dataMessages[indexPath!.row]["is_stared"] as? String ?? "" == "0") {
  3798. star = UIAction(title: "Star".localized(), image: UIImage(systemName: "star"), handler: {(_) in
  3799. if self.removed {
  3800. return
  3801. }
  3802. DispatchQueue.global().async {
  3803. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  3804. do {
  3805. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  3806. "is_stared" : 1
  3807. ], _where: "message_id = '\(dataMessages[indexPath!.row]["message_id"] as? String ?? "")'")
  3808. } catch {
  3809. rollback.pointee = true
  3810. print("Access database error: \(error.localizedDescription)")
  3811. }
  3812. })
  3813. }
  3814. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[indexPath!.row]["message_id"] as? String})
  3815. if idx != nil{
  3816. self.dataMessages[idx!]["is_stared"] = "1"
  3817. }
  3818. self.tableChatView.reloadRows(at: [indexPath!], with: .none)
  3819. NotificationCenter.default.post(name: NSNotification.Name(rawValue: "listenerStarMessage"), object: nil, userInfo: nil)
  3820. })
  3821. } else {
  3822. star = UIAction(title: "Unstar".localized(), image: UIImage(systemName: "star.slash"), handler: {(_) in
  3823. if self.removed {
  3824. return
  3825. }
  3826. DispatchQueue.global().async {
  3827. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  3828. do {
  3829. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  3830. "is_stared" : 0
  3831. ], _where: "message_id = '\(dataMessages[indexPath!.row]["message_id"] as? String ?? "")'")
  3832. } catch {
  3833. rollback.pointee = true
  3834. print("Access database error: \(error.localizedDescription)")
  3835. }
  3836. })
  3837. }
  3838. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[indexPath!.row]["message_id"] as? String})
  3839. if idx != nil{
  3840. self.dataMessages[idx!]["is_stared"] = "0"
  3841. }
  3842. self.tableChatView.reloadRows(at: [indexPath!], with: .none)
  3843. NotificationCenter.default.post(name: NSNotification.Name(rawValue: "listenerStarMessage"), object: nil, userInfo: nil)
  3844. })
  3845. }
  3846. let reply = UIAction(title: "Reply".localized(), image: UIImage(systemName: "arrowshape.turn.up.left"), handler: {(_) in
  3847. if self.removed {
  3848. return
  3849. }
  3850. if self.isSearching {
  3851. self.cancelAction()
  3852. }
  3853. DispatchQueue.main.asyncAfter(deadline: .now() + 0.35, execute: {
  3854. self.handleReply(indexPath: indexPath!)
  3855. })
  3856. })
  3857. let forward = UIAction(title: "Forward".localized(), image: UIImage(systemName: "arrowshape.turn.up.right"), handler: {(_) in
  3858. if self.removed {
  3859. return
  3860. }
  3861. if self.isSearching {
  3862. self.cancelAction()
  3863. }
  3864. if self.reffId != nil {
  3865. self.deleteReplyView()
  3866. }
  3867. DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
  3868. self.forwardSession = true
  3869. let cancelButton = UIBarButtonItem(title: "Cancel".localized(), style: .plain, target: self, action: #selector(self.cancelAction))
  3870. cancelButton.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16)], for: .normal)
  3871. if self.dataPerson["f_pin"] != "-999" && !self.isContactCenter {
  3872. self.navigationItem.rightBarButtonItems = nil
  3873. }
  3874. self.navigationItem.rightBarButtonItem = cancelButton
  3875. if self.isContactCenter || self.fromNotification {
  3876. self.navigationItem.leftBarButtonItem = nil
  3877. }
  3878. self.changeAppBar()
  3879. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[indexPath!.row]["message_id"] as? String})
  3880. if idx != nil{
  3881. self.dataMessages[idx!]["isSelected"] = true
  3882. }
  3883. self.addMultipleSelectSession()
  3884. self.tableChatView.reloadData()
  3885. }
  3886. })
  3887. let copy = UIAction(title: "Copy".localized(), image: UIImage(systemName: "doc.on.doc"), handler: {(_) in
  3888. if self.removed {
  3889. return
  3890. }
  3891. if self.isSearching {
  3892. self.cancelAction()
  3893. }
  3894. if self.reffId != nil {
  3895. self.deleteReplyView()
  3896. }
  3897. DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
  3898. self.copySession = true
  3899. let cancelButton = UIBarButtonItem(title: "Cancel".localized(), style: .plain, target: self, action: #selector(self.cancelAction))
  3900. cancelButton.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16)], for: .normal)
  3901. if self.dataPerson["f_pin"] != "-999" && !self.isContactCenter {
  3902. self.navigationItem.rightBarButtonItems = nil
  3903. }
  3904. self.navigationItem.rightBarButtonItem = cancelButton
  3905. if self.isContactCenter || self.fromNotification {
  3906. self.navigationItem.leftBarButtonItem = nil
  3907. }
  3908. self.changeAppBar()
  3909. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[indexPath!.row]["message_id"] as? String})
  3910. if idx != nil{
  3911. self.dataMessages[idx!]["isSelected"] = true
  3912. }
  3913. self.addMultipleSelectSession()
  3914. self.tableChatView.reloadData()
  3915. }
  3916. })
  3917. let edit = UIAction(title: "Edit".localized(), image: UIImage(systemName: "pencil.tip.crop.circle"), handler: {(_) in
  3918. self.isEditingMessage = true
  3919. self.showEditMessageView(at: indexPath!)
  3920. })
  3921. let translate = UIAction(title: "Translate".localized(), image: UIImage(systemName: "t.bubble"), handler: {(_) in
  3922. self.view.makeToast("Translating...".localized(), duration: 3)
  3923. var translation: String = "English"
  3924. let lang: String = SecureUserDefaults.shared.value(forKey: "i18n_language") ?? "en"
  3925. if lang == "id" {
  3926. translation = "Indonesia"
  3927. }
  3928. let payload: [String : Any] = [
  3929. "role": "user",
  3930. "content": dataMessages[indexPath!.row][TypeDataMessage.message_text]!!
  3931. ]
  3932. let parameter: [String : Any] = [
  3933. "use_video": "0",
  3934. "translate": translation,
  3935. "payload": [payload]
  3936. ]
  3937. DispatchQueue.global().async {
  3938. Utils.postDataWithCookiesAndUserAgent(from: URL(string: Utils.getGPTBotUrl())!, parameter: parameter, completion: { data, response, error in
  3939. let response = response as? HTTPURLResponse
  3940. if response?.statusCode != 200 || error != nil {
  3941. DispatchQueue.main.async {
  3942. self.view.makeToast("There is an error occurred while translating your message. Please try again or check your network connection.".localized(), duration: 3)
  3943. }
  3944. return
  3945. }
  3946. if let data = data, let responseString = String(data: data, encoding: .utf8) {
  3947. if let json = try? JSONSerialization.jsonObject(with: responseString.data(using: String.Encoding.utf8)!, options: JSONSerialization.ReadingOptions()) as? [String: String] {
  3948. let dataContent = json["content"]!
  3949. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[indexPath!.row]["message_id"] as? String})
  3950. if idx != nil{
  3951. self.dataMessages[idx!][TypeDataMessage.message_text] = dataMessages[indexPath!.row][TypeDataMessage.message_text] as? String ?? "" + "\n\n" + "%\(dataContent)%"
  3952. }
  3953. DispatchQueue.main.async{
  3954. self.tableChatView.reloadRows(at: [indexPath!], with: .none)
  3955. }
  3956. }
  3957. }
  3958. })
  3959. }
  3960. })
  3961. let gcs = UIAction(title: "Get Chat Suggestion".localized(), image: UIImage(systemName: "exclamationmark.bubble"), handler: {(_) in
  3962. self.view.makeToast("Getting chat suggestion...".localized(), duration: 3)
  3963. let payload: [String : Any] = [
  3964. "role": "user",
  3965. "content": dataMessages[indexPath!.row][TypeDataMessage.message_text]!!
  3966. ]
  3967. let parameter: [String : Any] = [
  3968. "use_video": "0",
  3969. "suggest": "1",
  3970. "payload": [payload]
  3971. ]
  3972. DispatchQueue.global().async {
  3973. Utils.postDataWithCookiesAndUserAgent(from: URL(string: Utils.getGPTBotUrl())!, parameter: parameter, completion: { data, response, error in
  3974. let response = response as? HTTPURLResponse
  3975. if response?.statusCode != 200 || error != nil {
  3976. DispatchQueue.main.async {
  3977. 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)
  3978. }
  3979. return
  3980. }
  3981. if let data = data, let responseString = String(data: data, encoding: .utf8) {
  3982. if let json = try? JSONSerialization.jsonObject(with: responseString.data(using: String.Encoding.utf8)!, options: JSONSerialization.ReadingOptions()) as? [String: Any] {
  3983. if let dataMessage = json["message"] as? [[String: Any]] {
  3984. if let dataContent = dataMessage[0]["content"] as? String {
  3985. DispatchQueue.main.async{
  3986. self.textFieldSend.text = dataContent
  3987. self.textFieldSend.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : UIColor.black
  3988. }
  3989. }
  3990. }
  3991. }
  3992. }
  3993. })
  3994. }
  3995. })
  3996. let more = UIMenu(title: "More...".localized(), children: [translate, gcs])
  3997. let info = UIAction(title: "Info".localized(), image: UIImage(systemName: "info.circle"), handler: {(_) in
  3998. if self.removed {
  3999. return
  4000. }
  4001. let messageInfoVC = MessageInfo()
  4002. messageInfoVC.data = dataMessages[indexPath!.row]
  4003. messageInfoVC.dataPerson = self.dataPerson
  4004. self.navigationController?.pushViewController(messageInfoVC, animated: true)
  4005. })
  4006. let delete = UIAction(title: "Delete".localized(), image: UIImage(systemName: "trash"), attributes: .destructive, handler: {(_) in
  4007. if self.removed {
  4008. return
  4009. }
  4010. if self.isSearching {
  4011. self.cancelAction()
  4012. }
  4013. if self.reffId != nil {
  4014. self.deleteReplyView()
  4015. }
  4016. DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
  4017. self.deleteSession = true
  4018. let cancelButton = UIBarButtonItem(title: "Cancel".localized(), style: .plain, target: self, action: #selector(self.cancelAction))
  4019. cancelButton.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16)], for: .normal)
  4020. if self.dataPerson["f_pin"] != "-999" && !self.isContactCenter {
  4021. self.navigationItem.rightBarButtonItems = nil
  4022. }
  4023. self.navigationItem.rightBarButtonItem = cancelButton
  4024. if self.isContactCenter || self.fromNotification {
  4025. self.navigationItem.leftBarButtonItem = nil
  4026. }
  4027. self.changeAppBar()
  4028. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[indexPath!.row]["message_id"] as? String})
  4029. if idx != nil{
  4030. self.dataMessages[idx!]["isSelected"] = true
  4031. }
  4032. self.addMultipleSelectSession()
  4033. self.tableChatView.reloadData()
  4034. }
  4035. })
  4036. let resend = UIAction(title: "Resend".localized(), image: UIImage(systemName: "arrow.clockwise"), handler: {(_) in
  4037. let messageId = dataMessages[indexPath!.row][TypeDataMessage.message_id] as? String ?? ""
  4038. let status = dataMessages[indexPath!.row][TypeDataMessage.status] as? String ?? ""
  4039. var idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String ?? "" == messageId })
  4040. if let idxMessageIdParent = self.groupImages.firstIndex(where: { $0.value.contains(where: { $0.messageId == messageId }) }) {
  4041. if let idxInImages = self.groupImages[idxMessageIdParent].value.firstIndex(where: { $0.messageId == messageId }) {
  4042. self.groupImages[idxMessageIdParent].value[idxInImages].status = "1"
  4043. self.groupImages[idxMessageIdParent].value[idxInImages].dataMessage[TypeDataMessage.status] = "1"
  4044. }
  4045. idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == self.groupImages[idxMessageIdParent].key })
  4046. }
  4047. if (idx != nil) {
  4048. do {
  4049. self.dataMessages[idx!][TypeDataMessage.status] = "1"
  4050. self.dataMessages[idx!][TypeDataMessage.progress] = 0.0
  4051. let section = self.dataDates.firstIndex(of: self.dataMessages[idx!]["chat_date"] as? String ?? "")
  4052. 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 })
  4053. if row != nil && section != nil {
  4054. self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
  4055. }
  4056. } catch {
  4057. }
  4058. }
  4059. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  4060. do {
  4061. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  4062. "status" : "1"
  4063. ], _where: "message_id = '\(messageId)'")
  4064. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE_STATUS", cvalues: [
  4065. "status" : "1"
  4066. ], _where: "message_id = '\(messageId)'")
  4067. } catch {
  4068. rollback.pointee = true
  4069. print("Access database error: \(error.localizedDescription)")
  4070. }
  4071. })
  4072. let message = CoreMessage_TMessageBank.sendMessage(message_id: messageId,
  4073. l_pin: dataMessages[indexPath!.row][TypeDataMessage.l_pin] as? String ?? "",
  4074. message_scope_id: dataMessages[indexPath!.row][TypeDataMessage.message_scope_id] as? String ?? "",
  4075. status: "1",
  4076. message_text: dataMessages[indexPath!.row][TypeDataMessage.message_text] as? String ?? "",
  4077. credential: dataMessages[indexPath!.row][TypeDataMessage.credential] as? String ?? "",
  4078. attachment_flag: dataMessages[indexPath!.row][TypeDataMessage.attachment_flag] as? String ?? "",
  4079. ex_blog_id: dataMessages[indexPath!.row][TypeDataMessage.blog_id] as? String ?? "",
  4080. message_large_text: "",
  4081. ex_format: "",
  4082. image_id: dataMessages[indexPath!.row][TypeDataMessage.image_id] as? String ?? "",
  4083. audio_id: dataMessages[indexPath!.row][TypeDataMessage.audio_id] as? String ?? "",
  4084. video_id: dataMessages[indexPath!.row][TypeDataMessage.video_id] as? String ?? "",
  4085. file_id: dataMessages[indexPath!.row][TypeDataMessage.file_id] as? String ?? "",
  4086. thumb_id: dataMessages[indexPath!.row][TypeDataMessage.thumb_id] as? String ?? "",
  4087. reff_id: dataMessages[indexPath!.row][TypeDataMessage.reff_id] as? String ?? "",
  4088. read_receipts: dataMessages[indexPath!.row][TypeDataMessage.read_receipts] as? String ?? "",
  4089. chat_id: dataMessages[indexPath!.row][TypeDataMessage.chat_id] as? String ?? "",
  4090. is_call_center: dataMessages[indexPath!.row][TypeDataMessage.is_call_center] as? String ?? "",
  4091. call_center_id: dataMessages[indexPath!.row][TypeDataMessage.call_center_id] as? String ?? "",
  4092. opposite_pin: dataMessages[indexPath!.row][TypeDataMessage.opposite_pin] as? String ?? "")
  4093. Nexilis.addQueueMessage(message: message)
  4094. })
  4095. var children: [UIMenuElement] = [star, reply, forward, copy, delete]
  4096. var isMore = false
  4097. // let copyOption = self.copyOption(indexPath: indexPath!)
  4098. let idMe = User.getMyPin() as String?
  4099. if dataMessages[indexPath!.row]["status"] as? String ?? "" == "0" {
  4100. children = [resend, delete]
  4101. } else if isContactCenter {
  4102. if dataMessages[indexPath!.row]["attachment_flag"] as? String ?? "" == "11" {
  4103. children = [reply]
  4104. }
  4105. else {
  4106. children = [reply, copy]
  4107. }
  4108. } 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" {
  4109. children = [delete]
  4110. } else if (groupImages[dataMessages[indexPath!.row]["message_id"] as? String ?? ""] != nil) {
  4111. forward.title = "Forward All".localized()
  4112. delete.title = "Delete All".localized()
  4113. children = [forward, delete]
  4114. } else if blocking == "1" || blocking == "-1" {
  4115. children = [star, forward, copy ,delete]
  4116. if !(dataMessages[indexPath!.row]["image_id"] as? String ?? "").isEmpty || !(dataMessages[indexPath!.row]["video_id"] as? String ?? "").isEmpty {
  4117. children = [star, forward ,delete]
  4118. }
  4119. if (dataMessages[indexPath!.row]["f_pin"] as? String ?? "") == idMe {
  4120. children.insert(info, at: children.count - 1)
  4121. }
  4122. }
  4123. 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 {
  4124. children = [star, reply, forward ,delete]
  4125. if (dataMessages[indexPath!.row]["f_pin"] as? String ?? "") == idMe {
  4126. children.insert(info, at: children.count - 1)
  4127. }
  4128. } else if dataMessages[indexPath!.row]["attachment_flag"] as? String ?? "" == "11" {
  4129. children = [reply, delete]
  4130. if (dataMessages[indexPath!.row]["f_pin"] as? String ?? "") == idMe {
  4131. children.insert(info, at: children.count - 1)
  4132. }
  4133. } else {
  4134. if (dataMessages[indexPath!.row]["f_pin"] as? String ?? "") == idMe {
  4135. children.insert(info, at: children.count - 1)
  4136. }
  4137. if !(dataMessages[indexPath!.row][TypeDataMessage.message_text] as? String ?? "").isEmpty {
  4138. if (dataMessages[indexPath!.row]["f_pin"] as? String ?? "") == idMe {
  4139. children.insert(edit, at: children.count - 1)
  4140. }
  4141. isMore = true
  4142. }
  4143. }
  4144. let mainMenu = UIMenu(title: "", options: [.displayInline],
  4145. children: children)
  4146. var menuForShow = UIMenu(title: "", children: [mainMenu])
  4147. if isMore {
  4148. menuForShow = UIMenu(title: "", children: [mainMenu, more])
  4149. }
  4150. return UIContextMenuConfiguration(identifier: nil,
  4151. previewProvider: nil) { _ in
  4152. return menuForShow
  4153. }
  4154. }
  4155. func showEditMessageView(at indexPath: IndexPath) {
  4156. let dataMessages = self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == dataDates[indexPath.section]})
  4157. let oldText = dataMessages[indexPath.row][TypeDataMessage.message_text] as? String ?? ""
  4158. editVC = UIViewController()
  4159. if let view = editVC.view {
  4160. view.backgroundColor = .clear
  4161. let blurView = UIView()
  4162. let blurEffect = UIBlurEffect(style: .systemUltraThinMaterialLight)
  4163. let blurEffectView = UIVisualEffectView(effect: blurEffect)
  4164. blurEffectView.frame = blurView.bounds
  4165. blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  4166. blurView.addSubview(blurEffectView)
  4167. blurView.sendSubviewToBack(blurEffectView)
  4168. view.addSubview(blurView)
  4169. blurView.anchor(top: view.topAnchor, left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor)
  4170. let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissEditVC))
  4171. tapGesture.cancelsTouchesInView = false
  4172. view.addGestureRecognizer(tapGesture)
  4173. editTextView = UITextView()
  4174. editTextView.layer.cornerRadius = textFieldSend.maxCornerRadius()
  4175. editTextView.layer.borderWidth = 1.0
  4176. editTextView.textColor = UIColor.black
  4177. editTextView.tintColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
  4178. editTextView.textContainerInset = UIEdgeInsets(top: 12, left: 20, bottom: 11, right: 40)
  4179. editTextView.layer.borderColor = UIColor.lightGray.withAlphaComponent(0.5).cgColor
  4180. editTextView.font = UIFont.systemFont(ofSize: 12)
  4181. editTextView.delegate = self
  4182. editTextView.allowsEditingTextAttributes = true
  4183. editTextView.backgroundColor = .clear
  4184. view.addSubview(editTextView)
  4185. editTextView.anchor(left: view.leftAnchor, right: view.rightAnchor, paddingLeft: 15, paddingRight: 15)
  4186. constraintBottomeditTextView = editTextView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -15)
  4187. constraintHeighteditTextView = editTextView.heightAnchor.constraint(equalToConstant: 40)
  4188. constraintBottomeditTextView.isActive = true
  4189. constraintHeighteditTextView.isActive = true
  4190. editTextView.text = oldText
  4191. editTextView.becomeFirstResponder()
  4192. let buttonSend = UIButton(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
  4193. 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)
  4194. buttonSend.circle()
  4195. buttonSend.actionHandle(controlEvents: .touchUpInside,
  4196. ForAction:{() -> Void in
  4197. let newText = self.editTextView.text ?? ""
  4198. if newText.trimmingCharacters(in: .whitespacesAndNewlines) != oldText {
  4199. let lastEdited = Int64(Date().currentTimeMillis())
  4200. 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)
  4201. Nexilis.addQueueMessage(message: message, isEditMessage: true)
  4202. DispatchQueue.global().async {
  4203. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  4204. do {
  4205. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  4206. "message_text" : newText,
  4207. "last_edited" : lastEdited
  4208. ], _where: "message_id = '\(dataMessages[indexPath.row]["message_id"] as? String ?? "")'")
  4209. NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
  4210. } catch {
  4211. rollback.pointee = true
  4212. print("Access database error: \(error.localizedDescription)")
  4213. }
  4214. })
  4215. }
  4216. let idx = self.dataMessages.firstIndex(where: { $0[TypeDataMessage.message_id] as? String == dataMessages[indexPath.row][TypeDataMessage.message_id] as? String})
  4217. if idx != nil{
  4218. self.dataMessages[idx!][TypeDataMessage.message_text] = newText
  4219. self.dataMessages[idx!][TypeDataMessage.last_edit] = lastEdited
  4220. self.tableChatView.reloadRows(at: [indexPath], with: .none)
  4221. }
  4222. }
  4223. })
  4224. buttonSend.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .mainColor
  4225. view.addSubview(buttonSend)
  4226. buttonSend.anchor(right: view.rightAnchor, paddingRight: 15, width: 40, height: 40)
  4227. constraintBottomSendEditTV = buttonSend.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -15)
  4228. constraintBottomSendEditTV.isActive = true
  4229. let viewMessage = UIView()
  4230. view.addSubview(viewMessage)
  4231. viewMessage.translatesAutoresizingMaskIntoConstraints = false
  4232. if (dataMessages[indexPath.row][TypeDataMessage.f_pin] as? String == User.getMyPin()) {
  4233. viewMessage.leftAnchor.constraint(greaterThanOrEqualTo: view.leftAnchor, constant: 60).isActive = true
  4234. viewMessage.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -15).isActive = true
  4235. viewMessage.backgroundColor = .blueBubbleColor
  4236. viewMessage.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner, .layerMinXMinYCorner]
  4237. } else {
  4238. viewMessage.rightAnchor.constraint(lessThanOrEqualTo: view.rightAnchor, constant: 60).isActive = true
  4239. viewMessage.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 15).isActive = true
  4240. viewMessage.backgroundColor = .whiteBubbleColor
  4241. viewMessage.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMinYCorner, .layerMaxXMaxYCorner]
  4242. }
  4243. viewMessage.bottomAnchor.constraint(equalTo: editTextView.topAnchor, constant: -15).isActive = true
  4244. viewMessage.heightAnchor.constraint(greaterThanOrEqualToConstant: 44).isActive = true
  4245. viewMessage.widthAnchor.constraint(greaterThanOrEqualToConstant: 46).isActive = true
  4246. viewMessage.layer.cornerRadius = 10.0
  4247. viewMessage.clipsToBounds = true
  4248. let messageText = UILabel()
  4249. messageText.numberOfLines = 0
  4250. messageText.lineBreakMode = .byWordWrapping
  4251. viewMessage.addSubview(messageText)
  4252. messageText.translatesAutoresizingMaskIntoConstraints = false
  4253. messageText.topAnchor.constraint(equalTo: viewMessage.topAnchor, constant: 15).isActive = true
  4254. messageText.leadingAnchor.constraint(equalTo: viewMessage.leadingAnchor, constant: 15).isActive = true
  4255. messageText.bottomAnchor.constraint(equalTo: viewMessage.bottomAnchor, constant: -15).isActive = true
  4256. messageText.trailingAnchor.constraint(equalTo: viewMessage.trailingAnchor, constant: -15).isActive = true
  4257. messageText.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
  4258. messageText.font = .systemFont(ofSize: 12)
  4259. messageText.text = oldText
  4260. }
  4261. editVC.modalTransitionStyle = .crossDissolve
  4262. editVC.modalPresentationStyle = .overFullScreen
  4263. self.present(editVC, animated: true)
  4264. }
  4265. @objc func dismissEditVC() {
  4266. self.isEditingMessage = false
  4267. editVC.dismiss(animated: true)
  4268. }
  4269. @objc func cancelAction() {
  4270. DispatchQueue.main.async {
  4271. if self.copySession {
  4272. self.copySession = false
  4273. } else if self.forwardSession {
  4274. self.forwardSession = false
  4275. } else if self.deleteSession {
  4276. self.deleteSession = false
  4277. } else if self.isSearching {
  4278. self.countMatchesSearch = 0
  4279. self.isSearching = false
  4280. }
  4281. if self.viewTextfield.isHidden {
  4282. self.viewTextfield.isHidden = false
  4283. }
  4284. if self.viewAttachment.isHidden {
  4285. self.viewAttachment.isHidden = false
  4286. }
  4287. if self.containerAction.isHidden {
  4288. self.containerAction.isHidden = false
  4289. }
  4290. if self.viewButton.isHidden {
  4291. self.viewButton.isHidden = false
  4292. }
  4293. if self.constraintBottomTableViewWithTextfield.constant == -60.0 {
  4294. self.constraintBottomTableViewWithTextfield.constant = self.constraintBottomTableViewWithTextfield.constant + 70
  4295. DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: {
  4296. if (self.currentIndexpath != nil) {
  4297. self.tableChatView.scrollToRow(at: IndexPath(row: self.currentIndexpath!.row, section: self.currentIndexpath!.section), at: .none, animated: true)
  4298. } else {
  4299. self.tableChatView.scrollToBottom()
  4300. }
  4301. })
  4302. }
  4303. let data = self.dataMessages.filter({ $0["isSelected"] as? Bool == true })
  4304. for i in 0..<data.count {
  4305. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == data[i]["message_id"] as? String})
  4306. if idx != nil{
  4307. self.dataMessages[idx!]["isSelected"] = false
  4308. }
  4309. }
  4310. self.tableChatView.reloadData()
  4311. self.setRightButtonItem()
  4312. self.changeAppBar()
  4313. if self.isContactCenter || self.fromNotification {
  4314. let backButton = UIBarButtonItem(image: UIImage(systemName: "chevron.backward"), style: .plain, target: self, action: #selector(self.didTapExit))
  4315. self.navigationItem.leftBarButtonItem = backButton
  4316. }
  4317. self.containerMultpileSelectSession.removeFromSuperview()
  4318. self.checkNewMessage(tableView: self.tableChatView)
  4319. }
  4320. }
  4321. private func addMultipleSelectSession() {
  4322. viewTextfield.isHidden = true
  4323. viewAttachment.isHidden = true
  4324. containerAction.isHidden = true
  4325. viewButton.isHidden = true
  4326. constraintBottomTableViewWithTextfield.constant = constraintBottomTableViewWithTextfield.constant - 70
  4327. view.addSubview(containerMultpileSelectSession)
  4328. containerMultpileSelectSession.translatesAutoresizingMaskIntoConstraints = false
  4329. constraintBottomContainerMultpileSelectSession = containerMultpileSelectSession.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0)
  4330. NSLayoutConstraint.activate([
  4331. containerMultpileSelectSession.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
  4332. containerMultpileSelectSession.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
  4333. constraintBottomContainerMultpileSelectSession,
  4334. containerMultpileSelectSession.heightAnchor.constraint(equalToConstant: 50)
  4335. ])
  4336. containerMultpileSelectSession.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : .white
  4337. addSubviewMultipleSession()
  4338. }
  4339. private func addSubviewMultipleSession() {
  4340. let container = UIView()
  4341. containerMultpileSelectSession.addSubview(container)
  4342. container.translatesAutoresizingMaskIntoConstraints = false
  4343. NSLayoutConstraint.activate([
  4344. container.leadingAnchor.constraint(equalTo: containerMultpileSelectSession.leadingAnchor),
  4345. container.trailingAnchor.constraint(equalTo:containerMultpileSelectSession.trailingAnchor),
  4346. container.bottomAnchor.constraint(equalTo: containerMultpileSelectSession.bottomAnchor),
  4347. container.heightAnchor.constraint(equalToConstant: 50)
  4348. ])
  4349. container.layer.shadowOpacity = 0.7
  4350. container.layer.shadowOffset = CGSize(width: 3, height: 3)
  4351. container.layer.shadowRadius = 3.0
  4352. container.layer.shadowColor = UIColor.black.cgColor
  4353. container.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : .secondaryColor
  4354. if !isSearching {
  4355. let title = UILabel()
  4356. container.addSubview(title)
  4357. title.translatesAutoresizingMaskIntoConstraints = false
  4358. NSLayoutConstraint.activate([
  4359. title.centerXAnchor.constraint(equalTo: container.centerXAnchor),
  4360. title.centerYAnchor.constraint(equalTo:container.centerYAnchor),
  4361. ])
  4362. let countSelected = dataMessages.filter({ $0["isSelected"] as? Bool == true }).count
  4363. title.text = "\(countSelected) " + "Selected".localized()
  4364. title.textColor = .mainColor
  4365. title.font = UIFont.systemFont(ofSize: 15.0).bold
  4366. let button = UIImageView()
  4367. container.addSubview(button)
  4368. button.translatesAutoresizingMaskIntoConstraints = false
  4369. NSLayoutConstraint.activate([
  4370. button.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 15),
  4371. button.centerYAnchor.constraint(equalTo:container.centerYAnchor),
  4372. button.widthAnchor.constraint(equalToConstant: 30),
  4373. button.heightAnchor.constraint(equalToConstant: 30),
  4374. ])
  4375. if copySession {
  4376. button.image = UIImage(systemName: "doc.on.doc")
  4377. if countSelected == 0 {
  4378. button.tintColor = .gray
  4379. } else {
  4380. button.tintColor = .mainColor
  4381. }
  4382. } else if forwardSession {
  4383. button.image = UIImage(systemName: "arrowshape.turn.up.right")
  4384. if countSelected == 0 {
  4385. button.tintColor = .gray
  4386. } else {
  4387. button.tintColor = .mainColor
  4388. }
  4389. } else if deleteSession {
  4390. button.image = UIImage(systemName: "trash")
  4391. if countSelected == 0 {
  4392. button.tintColor = .gray
  4393. } else {
  4394. button.tintColor = .red
  4395. }
  4396. }
  4397. let buttonGesture = UITapGestureRecognizer(target: self, action: #selector(sessionAction))
  4398. button.isUserInteractionEnabled = true
  4399. button.addGestureRecognizer(buttonGesture)
  4400. let selectedMessage = dataMessages.filter({ $0["isSelected"] as? Bool == true })
  4401. if selectedMessage.count > 0 {
  4402. for i in 0..<selectedMessage.count {
  4403. if let isGroupingImages = groupImages[selectedMessage[i]["message_id"] as? String ?? ""] {
  4404. title.text = "\(countSelected + (isGroupingImages.count - 1)) " + "Selected".localized()
  4405. }
  4406. }
  4407. }
  4408. } else {
  4409. buttonUp = UIButton()
  4410. container.addSubview(buttonUp)
  4411. buttonUp.translatesAutoresizingMaskIntoConstraints = false
  4412. NSLayoutConstraint.activate([
  4413. buttonUp.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 10),
  4414. buttonUp.centerYAnchor.constraint(equalTo:container.centerYAnchor),
  4415. buttonUp.widthAnchor.constraint(equalToConstant: 30),
  4416. buttonUp.heightAnchor.constraint(equalToConstant: 30),
  4417. ])
  4418. buttonUp.addTarget(self, action: #selector(upSearchText), for: .touchUpInside)
  4419. buttonDown = UIButton()
  4420. container.addSubview(buttonDown)
  4421. buttonDown.translatesAutoresizingMaskIntoConstraints = false
  4422. NSLayoutConstraint.activate([
  4423. buttonDown.leadingAnchor.constraint(equalTo: buttonUp.trailingAnchor, constant: 15),
  4424. buttonDown.centerYAnchor.constraint(equalTo:container.centerYAnchor),
  4425. buttonDown.widthAnchor.constraint(equalToConstant: 30),
  4426. buttonDown.heightAnchor.constraint(equalToConstant: 30),
  4427. ])
  4428. buttonDown.addTarget(self, action: #selector(downSearchText), for: .touchUpInside)
  4429. buttonUp.setImage(UIImage(systemName: "chevron.up"), for: .normal)
  4430. buttonUp.tintColor = .gray
  4431. buttonDown.setImage(UIImage(systemName: "chevron.down"), for: .normal)
  4432. buttonDown.tintColor = .gray
  4433. titleSearchMatches = UILabel()
  4434. container.addSubview(titleSearchMatches)
  4435. titleSearchMatches.translatesAutoresizingMaskIntoConstraints = false
  4436. NSLayoutConstraint.activate([
  4437. titleSearchMatches.centerXAnchor.constraint(equalTo: container.centerXAnchor),
  4438. titleSearchMatches.centerYAnchor.constraint(equalTo:container.centerYAnchor),
  4439. ])
  4440. titleSearchMatches.textColor = .mainColor
  4441. titleSearchMatches.font = UIFont.systemFont(ofSize: 15.0).bold
  4442. titleSearchMatches.isHidden = true
  4443. DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: {
  4444. self.searchBar.becomeFirstResponder()
  4445. })
  4446. }
  4447. }
  4448. @objc func upSearchText() {
  4449. scrollToFirstSearchMessage(indexScroll: lastScrollIdxSearch + 1)
  4450. }
  4451. @objc func downSearchText() {
  4452. scrollToFirstSearchMessage(indexScroll: lastScrollIdxSearch - 1)
  4453. }
  4454. @objc func sessionAction() {
  4455. if copySession {
  4456. let dataMessages = self.dataMessages.filter({ $0["isSelected"] as? Bool == true })
  4457. let countSelected = dataMessages.count
  4458. if countSelected == 0 {
  4459. return
  4460. }
  4461. var text = ""
  4462. for i in 0..<countSelected {
  4463. let stringDate = (dataMessages[i]["server_date"] as? String ?? "")
  4464. let date = Date(milliseconds: Int64(stringDate)!)
  4465. let formatterDate = DateFormatter()
  4466. let formatterTime = DateFormatter()
  4467. formatterDate.dateFormat = "dd/MM/yy"
  4468. formatterDate.locale = NSLocale(localeIdentifier: "id") as Locale?
  4469. formatterTime.dateFormat = "HH:mm"
  4470. formatterTime.locale = NSLocale(localeIdentifier: "id") as Locale?
  4471. let dataProfile = getDataProfile(message_id: dataMessages[i]["message_id"] as? String ?? "")
  4472. if text.isEmpty {
  4473. text = "*[\(formatterDate.string(from: date as Date)) \(formatterTime.string(from: date as Date))] \(dataProfile["name"]!):*\n\(dataMessages[i]["message_text"] as? String ?? "")"
  4474. } else {
  4475. 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 ?? "")"
  4476. }
  4477. }
  4478. text = text + "\n\n\nchat " + "Powered by Nexilis".localized()
  4479. DispatchQueue.main.async {
  4480. UIPasteboard.general.string = text
  4481. self.view.makeToast("Text coppied to clipboard".localized(), duration: 3)
  4482. }
  4483. cancelAction()
  4484. } else if forwardSession {
  4485. var dataMessages = self.dataMessages.filter({ $0["isSelected"] as! Bool == true })
  4486. let countSelected = dataMessages.count
  4487. if countSelected == 0 {
  4488. return
  4489. }
  4490. for i in 0..<countSelected {
  4491. if groupImages[dataMessages[i]["message_id"] as? String ?? ""] != nil {
  4492. var tempData = dataMessages
  4493. tempData.remove(at: 0)
  4494. let dataMessageInGrouping = (groupImages[dataMessages[i]["message_id"] as? String ?? ""]!).map({ $0.dataMessage })
  4495. tempData.insert(contentsOf: dataMessageInGrouping, at: i)
  4496. dataMessages = tempData
  4497. }
  4498. }
  4499. contactChatNav.modalPresentationStyle = .custom
  4500. contactChatNav.navigationBar.tintColor = .white
  4501. contactChatNav.navigationBar.barTintColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : .mainColor
  4502. contactChatNav.navigationBar.isTranslucent = false
  4503. let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
  4504. contactChatNav.navigationBar.titleTextAttributes = textAttributes
  4505. contactChatNav.view.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : .mainColor
  4506. if let controller = contactChatNav.viewControllers.first as? ContactChatViewController {
  4507. controller.isChooser = { [weak self] scope, pin in
  4508. if scope == "3" {
  4509. let editorPersonalVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorPersonalVC") as! EditorPersonal
  4510. editorPersonalVC.unique_l_pin = pin
  4511. editorPersonalVC.dataMessageForward = dataMessages
  4512. self?.navigationController?.replaceAllViewController(with: editorPersonalVC, animated: true)
  4513. } else {
  4514. let editorGroupVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorGroupVC") as! EditorGroup
  4515. editorGroupVC.unique_l_pin = pin
  4516. editorGroupVC.dataMessageForward = dataMessages
  4517. self?.navigationController?.replaceAllViewController(with: editorGroupVC, animated: true)
  4518. }
  4519. }
  4520. }
  4521. self.present(contactChatNav, animated: true, completion: nil)
  4522. } else if deleteSession {
  4523. let dataMessages = self.dataMessages.filter({ $0["isSelected"] as! Bool == true })
  4524. var countSelected = dataMessages.count
  4525. if countSelected == 0 {
  4526. return
  4527. }
  4528. for i in 0..<countSelected {
  4529. if let isGroupingImages = groupImages[dataMessages[i]["message_id"] as? String ?? ""] {
  4530. countSelected += (isGroupingImages.count - 1)
  4531. }
  4532. }
  4533. let alertController = LibAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
  4534. if let action = self.actionDelete(for: "me", title: "Delete".localized() + " \(countSelected) " + "For Me".localized(), dataMessages: dataMessages) {
  4535. alertController.addAction(action)
  4536. }
  4537. let idMe = User.getMyPin() as String?
  4538. let dataFilterFpin = dataMessages.filter({ $0["l_pin"] as? String == idMe})
  4539. let dataFilterLock = dataMessages.filter({ $0["lock"] as? String == "1" || $0["lock"] as? String == "2" })
  4540. // let statusDataRead = dataMessages.filter({ Int($0["status"] as? String ?? "")! >= 4})
  4541. let statusFailed = dataMessages.filter({ Int($0["status"] as? String ?? "")! == 0})
  4542. if dataFilterFpin.count == 0 && dataFilterLock.count == 0 && statusFailed.count == 0 {
  4543. if let action = self.actionDelete(for: "everyone", title: "Delete".localized() + " \(countSelected) " + "For Everyone".localized(), dataMessages: dataMessages) {
  4544. alertController.addAction(action)
  4545. }
  4546. }
  4547. alertController.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: nil))
  4548. self.present(alertController, animated: true)
  4549. }
  4550. }
  4551. private func getDataProfile(message_id: String) -> [String: String]{
  4552. var data: [String: String] = [:]
  4553. Database().database?.inTransaction({ fmdb, rollback in
  4554. if let c = Database().getRecords(fmdb: fmdb, query: "select f_display_name from MESSAGE where message_id = '\(message_id)'"), c.next() {
  4555. data["name"] = c.string(forColumnIndex: 0)!
  4556. c.close()
  4557. } else {
  4558. data["name"] = "Unknown".localized()
  4559. data["image_id"] = ""
  4560. }
  4561. })
  4562. return data
  4563. }
  4564. private func deleteMessage(l_pin: String, message_id: String, scope: String, type: String, chat: String) {
  4565. let tmessage = CoreMessage_TMessageBank.deleteMessage(l_pin: l_pin, messageId: message_id, scope: scope, type: type, chat: chat)
  4566. Nexilis.deleteQueueMessage(message: tmessage)
  4567. }
  4568. private func queryMessageReply(message_id: String) -> [String: Any?] {
  4569. var dataQuery: [String: Any] = [:]
  4570. Database().database?.inTransaction({ fmdb, rollback in
  4571. 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() {
  4572. dataQuery["message_id"] = c.string(forColumnIndex: 0) ?? ""
  4573. dataQuery["f_pin"] = c.string(forColumnIndex: 1) ?? ""
  4574. dataQuery["message_text"] = c.string(forColumnIndex: 2) ?? ""
  4575. dataQuery["attachment_flag"] = c.string(forColumnIndex: 3) ?? ""
  4576. dataQuery["thumb_id"] = c.string(forColumnIndex: 4) ?? ""
  4577. dataQuery["image_id"] = c.string(forColumnIndex: 5) ?? ""
  4578. dataQuery["video_id"] = c.string(forColumnIndex: 6) ?? ""
  4579. dataQuery["file_id"] = c.string(forColumnIndex: 7) ?? ""
  4580. c.close()
  4581. }
  4582. })
  4583. return dataQuery
  4584. }
  4585. @objc func segmentedControlValueChanged(_ sender: segmentedControllerObject) {
  4586. switch sender.selectedSegmentIndex {
  4587. case 0:
  4588. sender.navigation.viewControllers[0].children[1].view.isHidden = true
  4589. break;
  4590. case 1:
  4591. sender.navigation.viewControllers[0].children[1].view.isHidden = false
  4592. break;
  4593. default:
  4594. break;
  4595. }
  4596. }
  4597. private func copyOption(indexPath: IndexPath) -> UIMenu {
  4598. var ratingButtonTitles = ["Text".localized(), "Image".localized()]
  4599. if (dataMessages[indexPath.row]["message_text"] as? String ?? "").isEmpty {
  4600. ratingButtonTitles = ["Image".localized()]
  4601. }
  4602. let dataMessages = self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == dataDates[indexPath.section]})
  4603. let copyActions = ratingButtonTitles
  4604. .enumerated()
  4605. .map { index, title in
  4606. return UIAction(
  4607. title: title,
  4608. identifier: nil,
  4609. handler: {(_) in
  4610. if (dataMessages[indexPath.row]["message_text"] as? String ?? "").isEmpty {
  4611. DispatchQueue.main.async {
  4612. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  4613. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  4614. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  4615. if let dirPath = paths.first {
  4616. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(dataMessages[indexPath.row]["image_id"] as? String ?? "")
  4617. if FileManager.default.fileExists(atPath: imageURL.path) {
  4618. let image = UIImage(contentsOfFile: imageURL.path)
  4619. UIPasteboard.general.image = image
  4620. self.view.makeToast("Image coppied to clipboard".localized(), duration: 3)
  4621. } else if FileEncryption.shared.isSecureExists(filename: imageURL.lastPathComponent) {
  4622. do {
  4623. if let imageData = try FileEncryption.shared.readSecure(filename: imageURL.lastPathComponent) {
  4624. let image = UIImage(data: imageData)
  4625. UIPasteboard.general.image = image
  4626. self.view.makeToast("Image coppied to clipboard".localized(), duration: 3)
  4627. }
  4628. } catch {
  4629. }
  4630. }
  4631. }
  4632. }
  4633. return
  4634. }
  4635. if (index == 0) {
  4636. DispatchQueue.main.async {
  4637. UIPasteboard.general.string = dataMessages[indexPath.row]["message_text"] as? String
  4638. self.view.makeToast("Text coppied to clipboard".localized(), duration: 3)
  4639. }
  4640. } else {
  4641. DispatchQueue.main.async {
  4642. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  4643. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  4644. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  4645. if let dirPath = paths.first {
  4646. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(dataMessages[indexPath.row]["image_id"] as? String ?? "")
  4647. if FileManager.default.fileExists(atPath: imageURL.path) {
  4648. let image = UIImage(contentsOfFile: imageURL.path)
  4649. UIPasteboard.general.image = image
  4650. self.view.makeToast("Image coppied to clipboard".localized(), duration: 3)
  4651. } else if FileEncryption.shared.isSecureExists(filename: imageURL.lastPathComponent) {
  4652. do {
  4653. if let imageData = try FileEncryption.shared.readSecure(filename: imageURL.lastPathComponent) {
  4654. let image = UIImage(data: imageData)
  4655. UIPasteboard.general.image = image
  4656. self.view.makeToast("Image coppied to clipboard".localized(), duration: 3)
  4657. }
  4658. } catch {
  4659. }
  4660. }
  4661. }
  4662. }
  4663. }
  4664. self.dismissKeyboard()
  4665. })
  4666. }
  4667. return UIMenu(
  4668. title: "Copy".localized(),
  4669. image: UIImage(systemName: "doc.on.doc.fill"),
  4670. children: copyActions)
  4671. }
  4672. private func actionDelete(for type: String, title: String, dataMessages: [[String: Any?]]) -> UIAlertAction? {
  4673. return UIAlertAction(title: title, style: .destructive) { [unowned self] _ in
  4674. for i in 0..<dataMessages.count {
  4675. if (type == "me") {
  4676. if let groupingImages = groupImages[dataMessages[i]["message_id"] as? String ?? ""] {
  4677. for i in 0..<groupingImages.count {
  4678. self.deleteMessage(l_pin: groupingImages[i].lPin, message_id: groupingImages[i].messageId, scope: "3", type: "1", chat: "")
  4679. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == groupingImages[i].messageId })
  4680. if idx != nil {
  4681. self.dataMessages.remove(at: idx!)
  4682. if (idx == self.dataMessages.count - 1) {
  4683. NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
  4684. }
  4685. for i in 0..<dataDates.count {
  4686. if self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == dataDates[i] }).count == 0 {
  4687. dataDates.remove(at: i)
  4688. }
  4689. }
  4690. }
  4691. }
  4692. self.groupImages.removeValue(forKey: groupingImages[0].messageId)
  4693. } else {
  4694. self.deleteMessage(l_pin: dataMessages[i]["l_pin"] as? String ?? "", message_id: dataMessages[i]["message_id"] as? String ?? "", scope: "3", type: "1", chat: "")
  4695. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[i]["message_id"] as? String})
  4696. if idx != nil {
  4697. self.dataMessages.remove(at: idx!)
  4698. if (idx == self.dataMessages.count - 1) {
  4699. NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
  4700. }
  4701. for i in 0..<dataDates.count {
  4702. if self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == dataDates[i] }).count == 0 {
  4703. dataDates.remove(at: i)
  4704. }
  4705. }
  4706. }
  4707. }
  4708. } else {
  4709. if !CheckConnection.isConnectedToNetwork() || API.nGetCLXConnState() == 0 {
  4710. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  4711. imageView.tintColor = .white
  4712. 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)
  4713. banner.show()
  4714. } else {
  4715. if let groupingImages = groupImages[dataMessages[i]["message_id"] as? String ?? ""] {
  4716. for i in 0..<groupingImages.count {
  4717. self.deleteMessage(l_pin: groupingImages[i].lPin, message_id: groupingImages[i].messageId, scope: "3", type: "2", chat: "")
  4718. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == groupingImages[i].messageId})
  4719. if idx != nil {
  4720. self.dataMessages[idx!]["lock"] = "1"
  4721. self.dataMessages[idx!]["attachment_flag"] = "0"
  4722. self.dataMessages[idx!]["reff_id"] = ""
  4723. }
  4724. }
  4725. if let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == groupingImages[0].messageId}) {
  4726. var dataMessageInGrouping = (groupImages[dataMessages[i]["message_id"] as? String ?? ""]!).map({ $0.dataMessage })
  4727. dataMessageInGrouping.remove(at: 0)
  4728. self.dataMessages.insert(contentsOf: dataMessageInGrouping, at: idx+1)
  4729. self.groupImages.removeValue(forKey: groupingImages[0].messageId)
  4730. }
  4731. } else {
  4732. self.deleteMessage(l_pin: dataMessages[i]["l_pin"] as? String ?? "", message_id: dataMessages[i]["message_id"] as? String ?? "", scope: "3", type: "2", chat: "")
  4733. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[i]["message_id"] as? String})
  4734. if idx != nil {
  4735. self.dataMessages[idx!]["lock"] = "1"
  4736. self.dataMessages[idx!]["attachment_flag"] = "0"
  4737. self.dataMessages[idx!]["reff_id"] = ""
  4738. }
  4739. }
  4740. }
  4741. }
  4742. if self.listTimerCredential[dataMessages[i]["message_id"] as? String ?? ""] != nil {
  4743. self.listTimerCredential.removeValue(forKey: dataMessages[i]["message_id"] as? String ?? "")
  4744. self.timerCredential[dataMessages[i]["message_id"] as? String ?? ""]?.invalidate()
  4745. self.timerCredential.removeValue(forKey: dataMessages[i]["message_id"] as? String ?? "")
  4746. }
  4747. }
  4748. NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
  4749. cancelAction()
  4750. }
  4751. }
  4752. private func updateProfile() {
  4753. let idMe = User.getMyPin() as String?
  4754. DispatchQueue.global().async {
  4755. let message = CoreMessage_TMessageBank.getBatchBuddiesInfos(p_f_pin: idMe!, last_update: 0)
  4756. let _ = Nexilis.write(message: message)
  4757. }
  4758. }
  4759. private func generateQRCode(from string: String) -> UIImage? {
  4760. let data = string.data(using: String.Encoding.ascii)
  4761. if let filter = CIFilter(name: "CIQRCodeGenerator") {
  4762. filter.setValue(data, forKey: "inputMessage")
  4763. let transform = CGAffineTransform(scaleX: 3, y: 3)
  4764. if let output = filter.outputImage?.transformed(by: transform) {
  4765. return UIImage(ciImage: output)
  4766. }
  4767. }
  4768. return nil
  4769. }
  4770. @objc func deleteReplyView() {
  4771. if self.containerPreviewReply.isDescendant(of: self.viewTextfield) {
  4772. self.containerPreviewReply.subviews.forEach { $0.removeFromSuperview() }
  4773. self.containerPreviewReply.removeConstraints(self.containerPreviewReply.constraints)
  4774. self.containerPreviewReply.removeFromSuperview()
  4775. self.reffId = nil
  4776. UIView.animate(withDuration: 0.25, delay: 0.0, options: .curveEaseInOut, animations: {
  4777. self.constraintTopTextField.constant = self.constraintTopTextField.constant - 50
  4778. }, completion: nil)
  4779. }
  4780. }
  4781. @objc func removeLinkPreviewUntilEmptyTextView() {
  4782. isAlwaysHideLinkPreview = true
  4783. deleteLinkPreview()
  4784. }
  4785. @objc func deleteLinkPreview() {
  4786. if self.containerLink.isDescendant(of: self.viewTextfield) {
  4787. self.containerLink.subviews.forEach { $0.removeFromSuperview() }
  4788. self.containerLink.removeConstraints(self.containerLink.constraints)
  4789. self.containerLink.removeFromSuperview()
  4790. UIView.animate(withDuration: 0.25, delay: 0.0, options: .curveEaseInOut, animations: {
  4791. self.constraintTopTextField.constant = self.constraintTopTextField.constant - 80
  4792. }, completion: nil)
  4793. self.showingLink = ""
  4794. }
  4795. if self.reffId != nil {
  4796. self.bottomAnchorPreviewReply.isActive = false
  4797. self.bottomAnchorPreviewReply = self.containerPreviewReply.bottomAnchor.constraint(equalTo: self.textFieldSend.topAnchor)
  4798. self.bottomAnchorPreviewReply.isActive = true
  4799. }
  4800. }
  4801. }
  4802. //ECL
  4803. extension EditorPersonal: UICollectionViewDelegate, UICollectionViewDataSource {
  4804. public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
  4805. return 76
  4806. }
  4807. public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
  4808. let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellSticker", for: indexPath)
  4809. if (cell.contentView.subviews.count > 0) {
  4810. cell.contentView.subviews.forEach({ $0.removeFromSuperview() })
  4811. }
  4812. let imageSticker = UIImageView()
  4813. cell.contentView.addSubview(imageSticker)
  4814. imageSticker.translatesAutoresizingMaskIntoConstraints = false
  4815. NSLayoutConstraint.activate([
  4816. imageSticker.topAnchor.constraint(equalTo: cell.contentView.topAnchor),
  4817. imageSticker.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor),
  4818. imageSticker.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor),
  4819. imageSticker.trailingAnchor.constraint(equalTo: cell.contentView.trailingAnchor)
  4820. ])
  4821. var imageStickerBundle = UIImage(named: stickers[indexPath.row], in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  4822. if imageStickerBundle == nil {
  4823. imageStickerBundle = UIImage(named: stickers[indexPath.row], in: Bundle.resourcesMediaBundle(for: Nexilis.self), with: nil)
  4824. }
  4825. imageSticker.image = imageStickerBundle //resourcesMediaBundle
  4826. return cell
  4827. }
  4828. public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
  4829. sendChat(message_text: "sticker/\(stickers[indexPath.row])", attachment_flag: "11", viewController: self)
  4830. constraintBottomAttachment.constant = 0.0
  4831. self.viewSticker.removeConstraints(self.viewSticker.constraints)
  4832. self.viewSticker.removeFromSuperview()
  4833. }
  4834. }
  4835. //ETB
  4836. extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
  4837. // public func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
  4838. // checkNewMessage(tableView: tableView)
  4839. // }
  4840. public func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
  4841. if self.tableChatView.alpha != 1.0 {
  4842. UIView.animate(withDuration: 0.5, animations: {
  4843. self.tableChatView.alpha = 1.0
  4844. })
  4845. }
  4846. }
  4847. public func scrollViewDidScroll(_ scrollView: UIScrollView) {
  4848. lastY = scrollView.contentOffset.y
  4849. DispatchQueue.main.async { [self] in
  4850. checkNewMessage(tableView: self.tableChatView)
  4851. }
  4852. }
  4853. public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
  4854. if isContactCenter && indexPath.row == 0 && isRequestContactCenter {
  4855. return
  4856. }
  4857. let dataMessages = self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == dataDates[indexPath.section] })
  4858. if copySession || forwardSession || deleteSession {
  4859. if (dataMessages[indexPath.row]["attachment_flag"] as? String ?? "" != "0" || dataMessages[indexPath.row]["lock"] as? String == "1") && !forwardSession && !deleteSession {
  4860. return
  4861. }
  4862. 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 {
  4863. var file = dataMessages[indexPath.row]["image_id"] as? String ?? ""
  4864. if file.isEmpty {
  4865. file = dataMessages[indexPath.row]["video_id"] as? String ?? ""
  4866. if file.isEmpty {
  4867. file = dataMessages[indexPath.row]["file_id"] as? String ?? ""
  4868. }
  4869. }
  4870. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  4871. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  4872. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  4873. if let dirPath = paths.first {
  4874. let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(file)
  4875. if !FileManager.default.fileExists(atPath: fileURL.path) && !FileEncryption.shared.isSecureExists(filename: fileURL.lastPathComponent) {
  4876. return
  4877. }
  4878. }
  4879. }
  4880. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[indexPath.row]["message_id"] as? String})
  4881. if idx != nil {
  4882. self.dataMessages[idx!]["isSelected"] = !(self.dataMessages[idx!]["isSelected"] as! Bool)
  4883. self.tableChatView.reloadRows(at: [indexPath], with: .none)
  4884. }
  4885. containerMultpileSelectSession.subviews.forEach({ $0.removeFromSuperview() })
  4886. addSubviewMultipleSession()
  4887. return
  4888. }
  4889. let message = dataMessages[indexPath.row]
  4890. if let attachmentFlag = message["attachment_flag"], let attachmentFlag = attachmentFlag as? String {
  4891. if attachmentFlag == "27" || attachmentFlag == "26" {
  4892. let streamingController = (attachmentFlag == "27") ? QmeraCreateStreamingViewController() : CreateSeminarViewController()
  4893. if let messageText = message["message_text"],
  4894. let messageText = messageText as? String,
  4895. var json = try! JSONSerialization.jsonObject(with: messageText.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
  4896. if json["blog"] == nil {
  4897. json["blog"] = message["blog_id"] ?? nil
  4898. }
  4899. switch(attachmentFlag){
  4900. case "27":
  4901. (streamingController as! QmeraCreateStreamingViewController).data = json
  4902. default:
  4903. (streamingController as! CreateSeminarViewController).data = json
  4904. }
  4905. if json["by"] as? String != User.getMyPin() as String? {
  4906. switch(attachmentFlag){
  4907. case "27":
  4908. (streamingController as! QmeraCreateStreamingViewController).isJoin = true
  4909. default:
  4910. (streamingController as! CreateSeminarViewController).isJoin = true
  4911. }
  4912. }
  4913. }
  4914. let streamingNav = CustomNavigationController(rootViewController: streamingController)
  4915. streamingNav.modalPresentationStyle = .custom
  4916. streamingNav.navigationBar.tintColor = .white
  4917. streamingNav.navigationBar.barTintColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : .mainColor
  4918. streamingNav.navigationBar.isTranslucent = false
  4919. streamingNav.navigationBar.overrideUserInterfaceStyle = .dark
  4920. streamingNav.navigationBar.barStyle = .black
  4921. let cancelButtonAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.font : UIFont.systemFont(ofSize: 16)]
  4922. UIBarButtonItem.appearance().setTitleTextAttributes(cancelButtonAttributes, for: .normal)
  4923. let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
  4924. streamingNav.navigationBar.titleTextAttributes = textAttributes
  4925. streamingNav.view.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : .mainColor
  4926. streamingNav.navigationBar.isTranslucent = false
  4927. navigationController?.present(streamingNav, animated: true, completion: nil)
  4928. } else if message["message_scope_id"] as? String == "18" {
  4929. let formView = FormEditor()
  4930. let messageText = message["message_text"] as? String ?? ""
  4931. formView.jsonData = messageText
  4932. formView.dataMessage = message
  4933. formView.dataPerson = self.dataPerson
  4934. formView.modalPresentationStyle = .custom
  4935. formView.modalTransitionStyle = .crossDissolve
  4936. formView.view.backgroundColor = .black.withAlphaComponent(0.2)
  4937. self.present(formView, animated: true, completion: nil)
  4938. }
  4939. }
  4940. }
  4941. public func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
  4942. let containerView = UIView()
  4943. containerView.backgroundColor = .clear
  4944. let dateView = UIView()
  4945. containerView.addSubview(dateView)
  4946. dateView.translatesAutoresizingMaskIntoConstraints = false
  4947. var topAnchor = dateView.topAnchor.constraint(equalTo: containerView.topAnchor)
  4948. topAnchor = dateView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 10.0)
  4949. NSLayoutConstraint.activate([
  4950. topAnchor,
  4951. dateView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
  4952. dateView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor),
  4953. dateView.heightAnchor.constraint(equalToConstant: 30),
  4954. dateView.widthAnchor.constraint(greaterThanOrEqualToConstant: 60)
  4955. ])
  4956. dateView.backgroundColor = .orangeColor
  4957. dateView.layer.cornerRadius = 15.0
  4958. dateView.clipsToBounds = true
  4959. let labelDate = UILabel()
  4960. dateView.addSubview(labelDate)
  4961. labelDate.translatesAutoresizingMaskIntoConstraints = false
  4962. NSLayoutConstraint.activate([
  4963. labelDate.centerYAnchor.constraint(equalTo: dateView.centerYAnchor),
  4964. labelDate.centerXAnchor.constraint(equalTo: dateView.centerXAnchor),
  4965. labelDate.leadingAnchor.constraint(equalTo: dateView.leadingAnchor, constant: 10),
  4966. labelDate.trailingAnchor.constraint(equalTo: dateView.trailingAnchor, constant: -10),
  4967. ])
  4968. labelDate.textAlignment = .center
  4969. labelDate.textColor = .secondaryColor
  4970. labelDate.font = UIFont.systemFont(ofSize: 12, weight: .medium)
  4971. labelDate.text = dataDates[section]
  4972. if listViewOnSection.count == 0 || listViewOnSection.count - 1 < section {
  4973. listViewOnSection.append(containerView)
  4974. } else {
  4975. listViewOnSection.remove(at: section)
  4976. listViewOnSection.insert(containerView, at: section)
  4977. }
  4978. return containerView
  4979. }
  4980. public func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
  4981. return 40
  4982. }
  4983. public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  4984. let idMe = User.getMyPin() as String?
  4985. let dataMessages = dataMessages.filter({$0["chat_date"] as? String ?? "" == dataDates[indexPath.section]})
  4986. let profileMessage = UIImageView()
  4987. let cell = tableView.dequeueReusableCell(withIdentifier: "cellEditorPersonal", for: indexPath as IndexPath)
  4988. cell.contentView.subviews.forEach({ $0.removeConstraints($0.constraints) })
  4989. cell.contentView.subviews.forEach({ $0.removeFromSuperview() })
  4990. if isContactCenter && isRequestContactCenter && dataMessages[indexPath.row]["category_cc"] != nil {
  4991. cell.backgroundColor = .clear
  4992. cell.selectionStyle = .none
  4993. if dataMessages[indexPath.row]["category_cc"] is [CategoryCC] {
  4994. let category_cc = dataMessages[indexPath.row]["category_cc"] as! [CategoryCC]
  4995. profileMessage.frame.size = CGSize(width: 35, height: 35)
  4996. cell.contentView.addSubview(profileMessage)
  4997. profileMessage.translatesAutoresizingMaskIntoConstraints = false
  4998. profileMessage.topAnchor.constraint(equalTo: cell.contentView.topAnchor, constant: 5).isActive = true
  4999. profileMessage.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: 15).isActive = true
  5000. profileMessage.heightAnchor.constraint(equalToConstant: 37).isActive = true
  5001. profileMessage.widthAnchor.constraint(equalToConstant: 35).isActive = true
  5002. profileMessage.circle()
  5003. profileMessage.clipsToBounds = true
  5004. profileMessage.backgroundColor = .lightGray
  5005. profileMessage.image = UIImage(systemName: "person")
  5006. profileMessage.tintColor = .white
  5007. profileMessage.contentMode = .scaleAspectFit
  5008. getImage(name: dataPerson["picture"]!!, placeholderImage: UIImage(systemName: "person.circle.fill")!) { result, isDownloaded, image in
  5009. profileMessage.image = image
  5010. }
  5011. profileMessage.contentMode = .scaleAspectFill
  5012. let containerMessage = UIView()
  5013. cell.contentView.addSubview(containerMessage)
  5014. containerMessage.translatesAutoresizingMaskIntoConstraints = false
  5015. containerMessage.topAnchor.constraint(equalTo: profileMessage.bottomAnchor).isActive = true
  5016. containerMessage.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: 5).isActive = true
  5017. containerMessage.trailingAnchor.constraint(lessThanOrEqualTo: cell.contentView.trailingAnchor, constant: -60).isActive = true
  5018. containerMessage.widthAnchor.constraint(greaterThanOrEqualToConstant: 46).isActive = true
  5019. // containerMessage.backgroundColor = .grayColor
  5020. // containerMessage.layer.cornerRadius = 10.0
  5021. // containerMessage.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMinYCorner, .layerMaxXMaxYCorner]
  5022. // containerMessage.clipsToBounds = true
  5023. // let timeMessage = UILabel()
  5024. // cell.contentView.addSubview(timeMessage)
  5025. // timeMessage.translatesAutoresizingMaskIntoConstraints = false
  5026. // timeMessage.leadingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: 8).isActive = true
  5027. let messageText = UILabel()
  5028. containerMessage.addSubview(messageText)
  5029. messageText.translatesAutoresizingMaskIntoConstraints = false
  5030. messageText.numberOfLines = 0
  5031. messageText.lineBreakMode = .byWordWrapping
  5032. containerMessage.addSubview(messageText)
  5033. messageText.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 5).isActive = true
  5034. messageText.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
  5035. messageText.bottomAnchor.constraint(equalTo: containerMessage.bottomAnchor, constant: -5).isActive = true
  5036. messageText.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  5037. if category_cc[0].id.contains("level0_") || dataMessages[indexPath.row]["attachment_flag"] != nil && dataMessages[indexPath.row]["attachment_flag"] as? String ?? "" == "503" {
  5038. messageText.text = "Welcome to".localized() + " " + dataPerson["name"]!! + " " + "Contact Center".localized()
  5039. + "\n" + "Please choose your desired communication method...".localized()
  5040. } else if category_cc[0].id.contains("level1_") {
  5041. messageText.text = "Please select your Consultation Topic:".localized()
  5042. } else if !category_cc[0].id.contains("level1_") && dataMessages[indexPath.row]["attachment_flag"] == nil {
  5043. messageText.text = "Please select the type of topic that you chosen".localized()
  5044. } else if dataMessages[indexPath.row]["attachment_flag"] != nil && dataMessages[indexPath.row]["attachment_flag"] as? String ?? "" == "502" {
  5045. messageText.text = "Please select the information option:".localized()
  5046. } else {
  5047. 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()
  5048. }
  5049. messageText.font = UIFont.systemFont(ofSize: 14, weight: .medium)
  5050. messageText.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
  5051. // let date = Date()
  5052. // let formatter = DateFormatter()
  5053. // formatter.dateFormat = "HH:mm"
  5054. // formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
  5055. // timeMessage.text = formatter.string(from: date as Date)
  5056. // timeMessage.font = UIFont.systemFont(ofSize: 10, weight: .medium)
  5057. // timeMessage.textColor = .lightGray
  5058. let containerButton = UIView()
  5059. cell.contentView.addSubview(containerButton)
  5060. containerButton.translatesAutoresizingMaskIntoConstraints = false
  5061. containerButton.topAnchor.constraint(equalTo: containerMessage.bottomAnchor, constant: 5).isActive = true
  5062. containerButton.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor, constant: -5).isActive = true
  5063. containerButton.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: 15).isActive = true
  5064. containerButton.widthAnchor.constraint(equalToConstant: self.view!.frame.size.width * 0.9).isActive = true
  5065. containerButton.heightAnchor.constraint(greaterThanOrEqualToConstant: 55).isActive = true
  5066. containerButton.backgroundColor = .clear
  5067. // timeMessage.bottomAnchor.constraint(equalTo:containerButton.topAnchor, constant: -5).isActive = true
  5068. for i in 0..<category_cc.count {
  5069. let buttonChat = UIButton(type: .custom)
  5070. containerButton.addSubview(buttonChat)
  5071. buttonChat.translatesAutoresizingMaskIntoConstraints = false
  5072. buttonChat.widthAnchor.constraint(equalToConstant: self.view!.frame.size.width * 0.9 / 2 - 5).isActive = true
  5073. buttonChat.heightAnchor.constraint(greaterThanOrEqualToConstant: 55).isActive = true
  5074. if i % 2 == 0 {
  5075. if i / 2 + 1 == 1 {
  5076. buttonChat.topAnchor.constraint(equalTo: containerButton.topAnchor, constant: 5).isActive = true
  5077. } else {
  5078. var constantTop = (i / 2 - 1) * 50
  5079. if constantTop == 0 {
  5080. constantTop = 55
  5081. } else {
  5082. constantTop = constantTop + 55
  5083. }
  5084. buttonChat.topAnchor.constraint(equalTo: containerButton.topAnchor, constant: CGFloat(constantTop)).isActive = true
  5085. }
  5086. if i == category_cc.count - 1 {
  5087. buttonChat.bottomAnchor.constraint(equalTo: containerButton.bottomAnchor, constant: -5).isActive = true
  5088. }
  5089. buttonChat.leadingAnchor.constraint(equalTo: containerButton.leadingAnchor, constant: 5).isActive = true
  5090. } else {
  5091. let newi = i - 1
  5092. if newi / 2 + 1 == 1 {
  5093. buttonChat.topAnchor.constraint(equalTo: containerButton.topAnchor, constant: 5).isActive = true
  5094. } else {
  5095. var constantTop = (newi / 2 - 1) * 50
  5096. if constantTop == 0 {
  5097. constantTop = 55
  5098. } else {
  5099. constantTop = constantTop + 55
  5100. }
  5101. buttonChat.topAnchor.constraint(equalTo: containerButton.topAnchor, constant: CGFloat(constantTop)).isActive = true
  5102. }
  5103. if i == category_cc.count - 1 {
  5104. buttonChat.bottomAnchor.constraint(equalTo: containerButton.bottomAnchor, constant: -5).isActive = true
  5105. }
  5106. buttonChat.trailingAnchor.constraint(equalTo: containerButton.trailingAnchor, constant: -5).isActive = true
  5107. }
  5108. if category_cc[i].isActive {
  5109. buttonChat.backgroundColor = .orangeBNI
  5110. }
  5111. var nameImage = "pb_cc_bg_messaging"
  5112. if i == 1 {
  5113. nameImage = "pb_cc_bg_sms"
  5114. } else if i == 2 {
  5115. nameImage = "pb_cc_bg_voip"
  5116. } else if i == 3 {
  5117. nameImage = "pb_cc_bg_email"
  5118. } else if i == 4 {
  5119. nameImage = "pb_cc_bg_videocall"
  5120. } else if i == 5 {
  5121. nameImage = "pb_cc_bg_gsmcall"
  5122. } else if i == 6 {
  5123. nameImage = "pb_cc_bg_gptchatbot"
  5124. } else if i == 7 {
  5125. nameImage = "pb_cc_bg_whatsapp"
  5126. }
  5127. 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)
  5128. // buttonChat.setTitle(category_cc[i].service_name.localized(), for: .normal)
  5129. // buttonChat.setTitleColor(.black, for: .normal)
  5130. // 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)
  5131. // buttonChat.contentHorizontalAlignment = .left
  5132. // buttonChat.imageEdgeInsets = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 0) // Adjust left inset for the image
  5133. // buttonChat.titleEdgeInsets = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 0) // Adjust left inset for the title
  5134. // buttonChat.titleLabel?.font = UIFont.boldSystemFont(ofSize: 12)
  5135. // buttonChat.titleLabel?.numberOfLines = 0
  5136. // buttonChat.layer.borderWidth = 2
  5137. // buttonChat.layer.borderColor = UIColor.white.cgColor
  5138. // buttonChat.backgroundColor = .grayColor
  5139. // buttonChat.layer.cornerRadius = 8.0
  5140. // buttonChat.clipsToBounds = true
  5141. buttonChat.restorationIdentifier = "\(category_cc[i].id),\(category_cc[i].service_id)"
  5142. if dataMessages[indexPath.row]["attachment_flag"] != nil {
  5143. buttonChat.tag = Int(dataMessages[indexPath.row]["attachment_flag"] as? String ?? "")!
  5144. }
  5145. buttonChat.addTarget(self, action: #selector(ccAction(sender:)), for: .touchUpInside)
  5146. }
  5147. } else {
  5148. let messageWait = UILabel()
  5149. cell.contentView.addSubview(messageWait)
  5150. messageWait.translatesAutoresizingMaskIntoConstraints = false
  5151. messageWait.topAnchor.constraint(equalTo: cell.contentView.topAnchor).isActive = true
  5152. messageWait.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor).isActive = true
  5153. messageWait.leftAnchor.constraint(equalTo: cell.contentView.leftAnchor, constant: 10).isActive = true
  5154. messageWait.rightAnchor.constraint(equalTo: cell.contentView.rightAnchor, constant: -10).isActive = true
  5155. messageWait.text = dataMessages[indexPath.row]["category_cc"] as? String ?? dataMessages[indexPath.row]["message_text"] as? String ?? ""
  5156. messageWait.numberOfLines = 0
  5157. messageWait.font = UIFont.systemFont(ofSize: 12)
  5158. messageWait.textColor = .gray
  5159. messageWait.textAlignment = .center
  5160. }
  5161. return cell
  5162. }
  5163. let messageIdChat = (dataMessages[indexPath.row]["message_id"] as? String) ?? ""
  5164. let thumbChat = (dataMessages[indexPath.row]["thumb_id"] as? String) ?? ""
  5165. let imageChat = (dataMessages[indexPath.row]["image_id"] as? String) ?? ""
  5166. let videoChat = (dataMessages[indexPath.row]["video_id"] as? String) ?? ""
  5167. let fileChat = (dataMessages[indexPath.row]["file_id"] as? String) ?? ""
  5168. let reffChat = (dataMessages[indexPath.row]["reff_id"] as? String) ?? ""
  5169. let audioChat = (dataMessages[indexPath.row]["audio_id"] as? String) ?? ""
  5170. let gifChat = (dataMessages[indexPath.row]["gif_id"] as? String) ?? ""
  5171. let dataTimer = listTimerCredential[(dataMessages[indexPath.row]["message_id"] as? String ?? "")]
  5172. cell.backgroundColor = .clear
  5173. cell.selectionStyle = .none
  5174. let nameSender = UILabel()
  5175. if isContactCenter {
  5176. profileMessage.frame.size = CGSize(width: 35, height: 35)
  5177. cell.contentView.addSubview(profileMessage)
  5178. profileMessage.translatesAutoresizingMaskIntoConstraints = false
  5179. profileMessage.topAnchor.constraint(equalTo: cell.contentView.topAnchor, constant: 5).isActive = true
  5180. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  5181. profileMessage.trailingAnchor.constraint(equalTo: cell.contentView.trailingAnchor, constant: -15).isActive = true
  5182. } else {
  5183. if copySession || forwardSession || deleteSession {
  5184. profileMessage.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: 50).isActive = true
  5185. } else {
  5186. profileMessage.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: 15).isActive = true
  5187. }
  5188. }
  5189. profileMessage.heightAnchor.constraint(equalToConstant: 37).isActive = true
  5190. profileMessage.widthAnchor.constraint(equalToConstant: 35).isActive = true
  5191. profileMessage.circle()
  5192. profileMessage.clipsToBounds = true
  5193. profileMessage.backgroundColor = .lightGray
  5194. profileMessage.image = UIImage(systemName: "person")
  5195. profileMessage.tintColor = .white
  5196. profileMessage.contentMode = .scaleAspectFit
  5197. let user = User.getData(pin: dataMessages[indexPath.row]["f_pin"] as? String)
  5198. getImage(name: user?.thumb ?? "", placeholderImage: UIImage(systemName: "person.circle.fill")!, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
  5199. profileMessage.image = image
  5200. }
  5201. profileMessage.contentMode = .scaleAspectFill
  5202. cell.contentView.addSubview(nameSender)
  5203. nameSender.translatesAutoresizingMaskIntoConstraints = false
  5204. if markerCounter != nil && dataMessages[indexPath.row]["message_id"] as? String == markerCounter {
  5205. nameSender.topAnchor.constraint(equalTo: cell.contentView.topAnchor, constant: 35).isActive = true
  5206. } else {
  5207. nameSender.topAnchor.constraint(equalTo: cell.contentView.topAnchor, constant: 5).isActive = true
  5208. }
  5209. nameSender.font = UIFont.systemFont(ofSize: 12, weight: UIFont.Weight(800))
  5210. nameSender.text = user?.fullName ?? ""
  5211. nameSender.textAlignment = .right
  5212. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  5213. nameSender.trailingAnchor.constraint(equalTo:profileMessage.leadingAnchor, constant: -5).isActive = true
  5214. nameSender.textColor = .systemBlue
  5215. } else {
  5216. nameSender.leadingAnchor.constraint(equalTo:profileMessage.trailingAnchor, constant: 5).isActive = true
  5217. nameSender.textColor = .orangeColor
  5218. }
  5219. }
  5220. let containerMessage = UIView()
  5221. cell.contentView.addSubview(containerMessage)
  5222. containerMessage.translatesAutoresizingMaskIntoConstraints = false
  5223. let timeMessage = UILabel()
  5224. cell.contentView.addSubview(timeMessage)
  5225. timeMessage.translatesAutoresizingMaskIntoConstraints = false
  5226. if (dataMessages[indexPath.row]["read_receipts"] as? String) == "8" || ((dataMessages[indexPath.row]["credential"] as? String) == "1" && dataMessages[indexPath.row]["lock"] as? String != "2") {
  5227. timeMessage.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor, constant: -40).isActive = true
  5228. } else {
  5229. timeMessage.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor, constant: -5).isActive = true
  5230. }
  5231. let statusMessage = UIImageView()
  5232. if (dataMessages[indexPath.row]["attachment_flag"] as? String == "0" && dataMessages[indexPath.row]["lock"] as? String != "1") || forwardSession || deleteSession {
  5233. var showSelectedImage = true
  5234. if (!imageChat.isEmpty || !videoChat.isEmpty || !fileChat.isEmpty) && forwardSession {
  5235. var file = dataMessages[indexPath.row]["image_id"] as? String ?? ""
  5236. if file.isEmpty {
  5237. file = dataMessages[indexPath.row]["video_id"] as? String ?? ""
  5238. if file.isEmpty {
  5239. file = dataMessages[indexPath.row]["file_id"] as? String ?? ""
  5240. }
  5241. }
  5242. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  5243. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  5244. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  5245. if let dirPath = paths.first {
  5246. let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(file)
  5247. if !FileManager.default.fileExists(atPath: fileURL.path) && !FileEncryption.shared.isSecureExists(filename: fileURL.lastPathComponent) {
  5248. showSelectedImage = false
  5249. }
  5250. }
  5251. }
  5252. if showSelectedImage {
  5253. let selectedImage = UIImageView()
  5254. cell.contentView.addSubview(selectedImage)
  5255. selectedImage.translatesAutoresizingMaskIntoConstraints = false
  5256. selectedImage.frame.size = CGSize(width: 20, height: 20)
  5257. var leading = selectedImage.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: -20)
  5258. selectedImage.isHidden = true
  5259. if copySession || forwardSession || deleteSession {
  5260. leading = selectedImage.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: 15)
  5261. selectedImage.isHidden = false
  5262. }
  5263. NSLayoutConstraint.activate([
  5264. leading,
  5265. selectedImage.centerYAnchor.constraint(equalTo: cell.contentView.centerYAnchor),
  5266. selectedImage.widthAnchor.constraint(equalToConstant: 20),
  5267. selectedImage.heightAnchor.constraint(equalToConstant: 20)
  5268. ])
  5269. selectedImage.circle()
  5270. selectedImage.layer.borderWidth = 2
  5271. selectedImage.layer.borderColor = UIColor.mainColor.cgColor
  5272. if dataMessages[indexPath.row]["isSelected"] as! Bool {
  5273. selectedImage.image = UIImage(systemName: "checkmark.circle.fill")
  5274. }
  5275. selectedImage.tintColor = .mainColor
  5276. }
  5277. }
  5278. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  5279. containerMessage.leadingAnchor.constraint(greaterThanOrEqualTo: cell.contentView.leadingAnchor, constant: 60).isActive = true
  5280. if (dataMessages[indexPath.row]["read_receipts"] as? String) == "8" || ((dataMessages[indexPath.row]["credential"] as? String) == "1" && dataMessages[indexPath.row]["lock"] as? String != "2") {
  5281. containerMessage.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor, constant: -40).isActive = true
  5282. } else {
  5283. containerMessage.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor, constant: -5).isActive = true
  5284. }
  5285. if isContactCenter {
  5286. containerMessage.topAnchor.constraint(equalTo: nameSender.bottomAnchor).isActive = true
  5287. containerMessage.trailingAnchor.constraint(equalTo: profileMessage.leadingAnchor, constant: -5).isActive = true
  5288. } else {
  5289. containerMessage.topAnchor.constraint(equalTo: cell.contentView.topAnchor, constant: 5).isActive = true
  5290. containerMessage.trailingAnchor.constraint(equalTo: cell.contentView.trailingAnchor, constant: -15).isActive = true
  5291. }
  5292. containerMessage.widthAnchor.constraint(greaterThanOrEqualToConstant: 46).isActive = true
  5293. if (dataMessages[indexPath.row]["attachment_flag"] as? String == "11" && dataMessages[indexPath.row]["reff_id"]as? String == "") {
  5294. containerMessage.backgroundColor = .clear
  5295. } else {
  5296. containerMessage.backgroundColor = .blueBubbleColor
  5297. }
  5298. containerMessage.layer.cornerRadius = 10.0
  5299. containerMessage.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner, .layerMinXMinYCorner]
  5300. containerMessage.clipsToBounds = true
  5301. timeMessage.trailingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: -8).isActive = true
  5302. if (dataMessages[indexPath.row]["lock"] as? String != "2") {
  5303. cell.contentView.addSubview(statusMessage)
  5304. statusMessage.translatesAutoresizingMaskIntoConstraints = false
  5305. statusMessage.bottomAnchor.constraint(equalTo: timeMessage.topAnchor).isActive = true
  5306. statusMessage.trailingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: -8).isActive = true
  5307. statusMessage.widthAnchor.constraint(equalToConstant: 15).isActive = true
  5308. statusMessage.heightAnchor.constraint(equalToConstant: 15).isActive = true
  5309. if dataMessages[indexPath.row]["status"]! as? String ?? "" == "0" {
  5310. statusMessage.image = UIImage(systemName: "xmark.circle")!.withTintColor(UIColor.red, renderingMode: .alwaysOriginal)
  5311. } else if dataMessages[indexPath.row]["status"]! as? String ?? "" == "1" {
  5312. statusMessage.image = UIImage(systemName: "clock.arrow.circlepath")!.withTintColor(UIColor.lightGray, renderingMode: .alwaysOriginal)
  5313. } else if (dataMessages[indexPath.row]["status"]! as? String ?? "" == "2" ) {
  5314. statusMessage.image = UIImage(named: "checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.lightGray)
  5315. } else if (dataMessages[indexPath.row]["status"]! as? String ?? "" == "3") {
  5316. statusMessage.image = UIImage(named: "double-checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.lightGray)
  5317. } else if (dataMessages[indexPath.row]["status"]! as? String ?? "" == "8") {
  5318. statusMessage.image = UIImage(named: "message_status_ack", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withRenderingMode(.alwaysOriginal)
  5319. } else {
  5320. statusMessage.image = UIImage(named: "double-checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.systemBlue)
  5321. }
  5322. }
  5323. } else {
  5324. if markerCounter != nil && dataMessages[indexPath.row]["message_id"] as? String == markerCounter {
  5325. if isContactCenter {
  5326. containerMessage.topAnchor.constraint(equalTo: nameSender.bottomAnchor).isActive = true
  5327. } else {
  5328. containerMessage.topAnchor.constraint(equalTo: cell.contentView.topAnchor, constant: 35).isActive = true
  5329. }
  5330. let newMessagesView = UIView()
  5331. cell.contentView.addSubview(newMessagesView)
  5332. newMessagesView.translatesAutoresizingMaskIntoConstraints = false
  5333. NSLayoutConstraint.activate([
  5334. newMessagesView.topAnchor.constraint(equalTo: newMessagesView.topAnchor),
  5335. newMessagesView.bottomAnchor.constraint(equalTo: containerMessage.topAnchor),
  5336. newMessagesView.centerXAnchor.constraint(equalTo: cell.contentView.centerXAnchor),
  5337. newMessagesView.heightAnchor.constraint(equalToConstant: 30),
  5338. newMessagesView.widthAnchor.constraint(greaterThanOrEqualToConstant: 60)
  5339. ])
  5340. newMessagesView.backgroundColor = .greenColor
  5341. newMessagesView.layer.cornerRadius = 15.0
  5342. newMessagesView.clipsToBounds = true
  5343. let labelNewMessages = UILabel()
  5344. newMessagesView.addSubview(labelNewMessages)
  5345. labelNewMessages.translatesAutoresizingMaskIntoConstraints = false
  5346. NSLayoutConstraint.activate([
  5347. labelNewMessages.centerYAnchor.constraint(equalTo: newMessagesView.centerYAnchor),
  5348. labelNewMessages.centerXAnchor.constraint(equalTo: newMessagesView.centerXAnchor),
  5349. labelNewMessages.leadingAnchor.constraint(equalTo: newMessagesView.leadingAnchor, constant: 10),
  5350. labelNewMessages.trailingAnchor.constraint(equalTo: newMessagesView.trailingAnchor, constant: -10),
  5351. ])
  5352. labelNewMessages.textAlignment = .center
  5353. labelNewMessages.textColor = .secondaryColor
  5354. labelNewMessages.font = UIFont.systemFont(ofSize: 12, weight: .medium)
  5355. labelNewMessages.text = "Unread Messages".localized()
  5356. } else {
  5357. if isContactCenter {
  5358. containerMessage.topAnchor.constraint(equalTo: nameSender.bottomAnchor).isActive = true
  5359. } else {
  5360. containerMessage.topAnchor.constraint(equalTo: cell.contentView.topAnchor, constant: 5).isActive = true
  5361. }
  5362. }
  5363. if isContactCenter {
  5364. containerMessage.leadingAnchor.constraint(equalTo: profileMessage.trailingAnchor, constant: 5).isActive = true
  5365. } else {
  5366. if copySession || forwardSession || deleteSession {
  5367. containerMessage.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: 50).isActive = true
  5368. } else {
  5369. containerMessage.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: 15).isActive = true
  5370. }
  5371. }
  5372. if (dataMessages[indexPath.row]["read_receipts"] as? String) == "8" || ((dataMessages[indexPath.row]["credential"] as? String) == "1" && dataMessages[indexPath.row]["lock"] as? String != "2") {
  5373. containerMessage.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor, constant: -40).isActive = true
  5374. } else {
  5375. containerMessage.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor, constant: -5).isActive = true
  5376. }
  5377. containerMessage.trailingAnchor.constraint(lessThanOrEqualTo: cell.contentView.trailingAnchor, constant: -60).isActive = true
  5378. containerMessage.widthAnchor.constraint(greaterThanOrEqualToConstant: 46).isActive = true
  5379. 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") {
  5380. containerMessage.backgroundColor = .clear
  5381. } else {
  5382. containerMessage.backgroundColor = .whiteBubbleColor
  5383. }
  5384. containerMessage.layer.cornerRadius = 10.0
  5385. containerMessage.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMinYCorner, .layerMaxXMaxYCorner]
  5386. containerMessage.clipsToBounds = true
  5387. timeMessage.leadingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: 8).isActive = true
  5388. }
  5389. let imageStared = UIImageView()
  5390. if dataMessages[indexPath.row]["is_stared"] as? String == "1" && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as? String ?? "" == "0") {
  5391. cell.contentView.addSubview(imageStared)
  5392. imageStared.translatesAutoresizingMaskIntoConstraints = false
  5393. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  5394. imageStared.bottomAnchor.constraint(equalTo: statusMessage.topAnchor).isActive = true
  5395. imageStared.trailingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: -8).isActive = true
  5396. } else {
  5397. imageStared.bottomAnchor.constraint(equalTo: timeMessage.topAnchor).isActive = true
  5398. imageStared.leadingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: 8).isActive = true
  5399. }
  5400. imageStared.widthAnchor.constraint(equalToConstant: 15).isActive = true
  5401. imageStared.heightAnchor.constraint(equalToConstant: 15).isActive = true
  5402. imageStared.image = UIImage(systemName: "star.fill")
  5403. imageStared.backgroundColor = .clear
  5404. imageStared.tintColor = .systemYellow
  5405. }
  5406. if dataMessages[indexPath.row]["read_receipts"] as? String == "8" {
  5407. let imageAckView = UIImageView()
  5408. var imageAck = UIImage(named: "ack_icon_gray", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withRenderingMode(.alwaysOriginal)
  5409. if dataMessages[indexPath.row]["status"] as? String == "8" {
  5410. imageAck = UIImage(named: "ack_icon", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withRenderingMode(.alwaysOriginal)
  5411. }
  5412. imageAckView.image = imageAck
  5413. cell.contentView.addSubview(imageAckView)
  5414. imageAckView.translatesAutoresizingMaskIntoConstraints = false
  5415. imageAckView.widthAnchor.constraint(equalToConstant: 30).isActive = true
  5416. imageAckView.heightAnchor.constraint(equalToConstant: 30).isActive = true
  5417. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  5418. imageAckView.topAnchor.constraint(equalTo: containerMessage.bottomAnchor, constant: 5).isActive = true
  5419. imageAckView.trailingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 30).isActive = true
  5420. } else {
  5421. imageAckView.topAnchor.constraint(equalTo: containerMessage.bottomAnchor, constant: 5).isActive = true
  5422. imageAckView.leadingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -30).isActive = true
  5423. let tap = ObjectGesture(target: self, action: #selector(tapAck(_:)))
  5424. tap.indexPath = indexPath
  5425. imageAckView.addGestureRecognizer(tap)
  5426. imageAckView.isUserInteractionEnabled = true
  5427. }
  5428. }
  5429. if (dataMessages[indexPath.row]["credential"] as? String) == "1" && (dataMessages[indexPath.row]["lock"] as? String) != "2" {
  5430. let imageCredentialView = UIImageView()
  5431. let imageCredential = UIImage(named: "confidential_icon", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withRenderingMode(.alwaysOriginal)
  5432. imageCredentialView.image = imageCredential
  5433. cell.contentView.addSubview(imageCredentialView)
  5434. imageCredentialView.translatesAutoresizingMaskIntoConstraints = false
  5435. imageCredentialView.widthAnchor.constraint(equalToConstant: 30).isActive = true
  5436. imageCredentialView.heightAnchor.constraint(equalToConstant: 30).isActive = true
  5437. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  5438. imageCredentialView.topAnchor.constraint(equalTo: containerMessage.bottomAnchor, constant: 5).isActive = true
  5439. imageCredentialView.trailingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 30).isActive = true
  5440. } else {
  5441. imageCredentialView.topAnchor.constraint(equalTo: containerMessage.bottomAnchor, constant: 5).isActive = true
  5442. imageCredentialView.leadingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -30).isActive = true
  5443. }
  5444. }
  5445. if dataMessages[indexPath.row][TypeDataMessage.last_edit] != nil && dataMessages[indexPath.row][TypeDataMessage.last_edit] as! Int64 != 0 {
  5446. let editedText = UILabel()
  5447. editedText.text = "Edited".localized()
  5448. editedText.font = UIFont.systemFont(ofSize: 10, weight: .medium)
  5449. editedText.textColor = .lightGray
  5450. cell.contentView.addSubview(editedText)
  5451. editedText.translatesAutoresizingMaskIntoConstraints = false
  5452. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  5453. editedText.trailingAnchor.constraint(equalTo: timeMessage.leadingAnchor, constant: -2).isActive = true
  5454. } else {
  5455. editedText.leadingAnchor.constraint(equalTo: timeMessage.trailingAnchor, constant: 2).isActive = true
  5456. }
  5457. editedText.bottomAnchor.constraint(equalTo: containerMessage.bottomAnchor).isActive = true
  5458. }
  5459. let messageText = UITextView()
  5460. messageText.isEditable = false
  5461. messageText.isSelectable = true
  5462. messageText.dataDetectorTypes = []
  5463. messageText.backgroundColor = .clear
  5464. messageText.isScrollEnabled = false
  5465. messageText.textContainerInset = UIEdgeInsets.zero
  5466. messageText.contentInset = UIEdgeInsets.zero
  5467. messageText.textDragInteraction?.isEnabled = false
  5468. containerMessage.addSubview(messageText)
  5469. messageText.translatesAutoresizingMaskIntoConstraints = false
  5470. let topMarginText = messageText.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 15)
  5471. messageText.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
  5472. messageText.font = .systemFont(ofSize: 12)
  5473. 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" {
  5474. messageText.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 85).isActive = true
  5475. let imageLS = UIImageView()
  5476. containerMessage.addSubview(imageLS)
  5477. imageLS.translatesAutoresizingMaskIntoConstraints = false
  5478. NSLayoutConstraint.activate([
  5479. imageLS.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15.0),
  5480. imageLS.trailingAnchor.constraint(equalTo: messageText.leadingAnchor, constant: -10.0),
  5481. imageLS.centerYAnchor.constraint(equalTo: containerMessage.centerYAnchor),
  5482. imageLS.heightAnchor.constraint(equalToConstant: 60.0)
  5483. ])
  5484. if dataMessages[indexPath.row]["attachment_flag"] as? String ?? "" == "26" {
  5485. imageLS.image = UIImage(named: "pb_seminar_wpr", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  5486. } else if dataMessages[indexPath.row]["attachment_flag"] as? String ?? "" == "27" {
  5487. imageLS.image = UIImage(named: "pb_live_tv", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  5488. } else if dataMessages[indexPath.row]["message_scope_id"] as? String == "18" {
  5489. imageLS.image = UIImage(systemName: "doc.richtext.fill")
  5490. imageLS.tintColor = .mainColor
  5491. }
  5492. } else if !audioChat.isEmpty {
  5493. messageText.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 60).isActive = true
  5494. } else {
  5495. messageText.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
  5496. }
  5497. 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") {
  5498. messageText.bottomAnchor.constraint(equalTo: containerMessage.bottomAnchor, constant: -115).isActive = true
  5499. let imageQR = UIImageView()
  5500. containerMessage.addSubview(imageQR)
  5501. imageQR.translatesAutoresizingMaskIntoConstraints = false
  5502. NSLayoutConstraint.activate([
  5503. imageQR.centerXAnchor.constraint(equalTo: containerMessage.centerXAnchor),
  5504. imageQR.topAnchor.constraint(equalTo: messageText.bottomAnchor),
  5505. imageQR.widthAnchor.constraint(equalToConstant: 100.0),
  5506. imageQR.heightAnchor.constraint(equalToConstant: 100.0)
  5507. ])
  5508. imageQR.image = generateQRCode(from: dataMessages[indexPath.row]["blog_id"] as? String ?? "")
  5509. } else {
  5510. messageText.bottomAnchor.constraint(equalTo: containerMessage.bottomAnchor, constant: -15).isActive = true
  5511. }
  5512. messageText.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  5513. var textChat = (dataMessages[indexPath.row]["message_text"] as? String) ?? ""
  5514. if (dataMessages[indexPath.row]["lock"] != nil && (dataMessages[indexPath.row]["lock"])! as? String == "1") {
  5515. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  5516. textChat = "🚫 _"+"You were deleted this message".localized()+"_"
  5517. } else {
  5518. textChat = "🚫 _"+"This message was deleted".localized()+"_"
  5519. }
  5520. }
  5521. if dataMessages[indexPath.row]["lock"] as? String == "2" {
  5522. textChat = "🚫 _"+"Message has expired".localized()+"_"
  5523. }
  5524. if !audioChat.isEmpty {
  5525. textChat = textChat.components(separatedBy: "|")[0]
  5526. }
  5527. let imageSticker = UIImageView()
  5528. var stringLS = ""
  5529. if let attachmentFlag = dataMessages[indexPath.row]["attachment_flag"], let attachmentFlag = attachmentFlag as? String {
  5530. if attachmentFlag == "27" || attachmentFlag == "26" { // live streaming
  5531. let data = textChat
  5532. if let json = try! JSONSerialization.jsonObject(with: data.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
  5533. let title = json["title"] as? String ?? ""
  5534. let description = json["description"] as? String ?? ""
  5535. let start = json["time"] as? Int64 ?? 0
  5536. let by = json["by"] as? String ?? ""
  5537. let textLS = "Live Streaming".localized()
  5538. var type = "*\(textLS)*"
  5539. if attachmentFlag == "26" {
  5540. let textSeminar = "Seminar".localized()
  5541. type = "*\(textSeminar)*"
  5542. }
  5543. if let c = User.getData(pin: by) {
  5544. let name = c.fullName
  5545. stringLS = "\(type) \nTitle: \(title) \nDescription: \(description) \nStart: \(Date(milliseconds: start).format(dateFormat: "dd/MM/yyyy HH:mm")) \nBroadcaster: \(name)"
  5546. } else {
  5547. stringLS = ("\(type) \nTitle: \(title) \nDescription: \(description) \nStart: \(Date(milliseconds: start).format(dateFormat: "dd/MM/yyyy HH:mm"))")
  5548. }
  5549. messageText.attributedText = stringLS.richText()
  5550. messageText.isUserInteractionEnabled = false
  5551. }
  5552. }
  5553. else if attachmentFlag == "11" && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as? String ?? "" != "1") && (dataMessages[indexPath.row]["lock"] as? String != "2") {
  5554. messageText.text = ""
  5555. topMarginText.constant = topMarginText.constant + 100
  5556. containerMessage.addSubview(imageSticker)
  5557. imageSticker.translatesAutoresizingMaskIntoConstraints = false
  5558. let data = queryMessageReply(message_id: reffChat)
  5559. if reffChat.isEmpty || data.count == 0 {
  5560. imageSticker.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 15).isActive = true
  5561. imageSticker.widthAnchor.constraint(equalToConstant: 80).isActive = true
  5562. } else {
  5563. imageSticker.widthAnchor.constraint(greaterThanOrEqualToConstant: 80).isActive = true
  5564. }
  5565. imageSticker.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
  5566. imageSticker.bottomAnchor.constraint(equalTo: messageText.topAnchor, constant: -5).isActive = true
  5567. imageSticker.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  5568. var imageStickerBundle = UIImage(named: (textChat.components(separatedBy: "/")[1]), in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  5569. if imageStickerBundle == nil {
  5570. imageStickerBundle = UIImage(named: (textChat.components(separatedBy: "/")[1]), in: Bundle.resourcesMediaBundle(for: Nexilis.self), with: nil)
  5571. }
  5572. imageSticker.image = imageStickerBundle //resourcesMediaBundle
  5573. imageSticker.contentMode = .scaleAspectFit
  5574. } else if dataMessages[indexPath.row]["message_scope_id"] as? String ?? "" == "18" {
  5575. let data = textChat
  5576. if let jsonForm = try! JSONSerialization.jsonObject(with: data.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
  5577. let form_title = jsonForm["form_title"] as? String ?? ""
  5578. let club_type = jsonForm["club_type"] as? String ?? ""
  5579. let province = jsonForm["province"] as? String ?? ""
  5580. let club = jsonForm["club"] as? String ?? ""
  5581. messageText.attributedText = "*\(form_title.replacingOccurrences(of: "+", with: " "))* \nClub Type: \(club_type) \nProvince: \(province) \nClub Name: \(club) ".richText()
  5582. messageText.isUserInteractionEnabled = false
  5583. }
  5584. }
  5585. else {
  5586. messageText.attributedText = textChat.richText()
  5587. modifyText()
  5588. }
  5589. } else {
  5590. messageText.attributedText = textChat.richText()
  5591. modifyText()
  5592. }
  5593. func modifyText() {
  5594. if !textChat.isEmpty {
  5595. if textChat.contains("■"){
  5596. textChat = textChat.components(separatedBy: "■")[0]
  5597. textChat = textChat.trimmingCharacters(in: .whitespacesAndNewlines)
  5598. }
  5599. let finalAtribute = textChat.richText()
  5600. textChat = finalAtribute.string
  5601. let urlPattern = "(https?://|www\\.)\\S+"
  5602. if let regex = try? NSRegularExpression(pattern: urlPattern, options: []) {
  5603. let matches = regex.matches(in: textChat, options: [], range: NSRange(textChat.startIndex..., in: textChat))
  5604. for match in matches {
  5605. if let range = Range(match.range, in: textChat) {
  5606. let linkText = String(textChat[range])
  5607. let nsRange = NSRange(range, in: textChat)
  5608. finalAtribute.addAttribute(.link, value: linkText, range: nsRange)
  5609. finalAtribute.addAttribute(.foregroundColor, value: UIColor.blue, range: nsRange)
  5610. finalAtribute.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: nsRange)
  5611. }
  5612. }
  5613. }
  5614. messageText.attributedText = finalAtribute
  5615. messageText.delegate = self
  5616. }
  5617. }
  5618. if !copySession && !forwardSession && !deleteSession && !self.removed {
  5619. let interaction = UIContextMenuInteraction(delegate: self)
  5620. containerMessage.addInteraction(interaction)
  5621. containerMessage.isUserInteractionEnabled = true
  5622. }
  5623. if isSearching && textSearch.count > 1 {
  5624. messageText.attributedText = stringLS.isEmpty ? textChat.richText(isSearching: true, textSearch: textSearch) : stringLS.richText(isSearching: true, textSearch: textSearch)
  5625. if textChat.lowercased().contains(textSearch) {
  5626. countMatchesSearch += 1
  5627. }
  5628. }
  5629. var stringDate = (dataMessages[indexPath.row]["server_date"] as? String) ?? ""
  5630. if dataMessages[indexPath.row][TypeDataMessage.last_edit] != nil && dataMessages[indexPath.row][TypeDataMessage.last_edit] as! Int64 != 0 {
  5631. stringDate = "\(dataMessages[indexPath.row][TypeDataMessage.last_edit] as! Int64)"
  5632. }
  5633. if !stringDate.isEmpty {
  5634. if (dataMessages[indexPath.row]["credential"] as? String) == "1" && dataMessages[indexPath.row]["lock"] as? String != "2" {
  5635. if dataTimer! >= 10 {
  5636. timeMessage.text = "00:\(dataTimer!)"
  5637. } else {
  5638. timeMessage.text = "00:0\(dataTimer!)"
  5639. }
  5640. timeMessage.textColor = .systemRed
  5641. } else {
  5642. let date = Date(milliseconds: Int64(stringDate) ?? 100)
  5643. let formatter = DateFormatter()
  5644. formatter.dateFormat = "HH:mm"
  5645. formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
  5646. timeMessage.text = formatter.string(from: date as Date)
  5647. timeMessage.textColor = .lightGray
  5648. }
  5649. timeMessage.font = UIFont.systemFont(ofSize: 10, weight: .medium)
  5650. }
  5651. let imageThumb = UIImageView()
  5652. let containerViewFile = UIView()
  5653. let imageGif = SDAnimatedImageView()
  5654. if !audioChat.isEmpty {
  5655. let imageAudio = UIImageView()
  5656. imageAudio.image = UIImage(systemName: "music.note", withConfiguration: UIImage.SymbolConfiguration(pointSize: 35))
  5657. containerMessage.addSubview(imageAudio)
  5658. imageAudio.anchor(left: containerMessage.leftAnchor, paddingLeft: 15, centerY: containerMessage.centerYAnchor)
  5659. imageAudio.tintColor = .black
  5660. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  5661. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  5662. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  5663. if let dirPath = paths.first {
  5664. let audioURL = URL(fileURLWithPath: dirPath).appendingPathComponent(audioChat)
  5665. if !FileManager.default.fileExists(atPath: audioURL.path) && !FileEncryption.shared.isSecureExists(filename: audioChat) {
  5666. Download().startHTTP(forKey: audioChat, isImage: false) { (name, progress) in
  5667. guard progress == 100 else {
  5668. return
  5669. }
  5670. tableView.reloadRows(at: [indexPath], with: .none)
  5671. }
  5672. }
  5673. }
  5674. let objectTap = ObjectGesture(target: self, action: #selector(contentMessageTapped(_:)))
  5675. containerMessage.addGestureRecognizer(objectTap)
  5676. objectTap.audio_id = audioChat
  5677. }
  5678. if (!thumbChat.isEmpty && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as? String ?? "" != "1") && (dataMessages[indexPath.row]["lock"] as? String != "2")) {
  5679. if let listImages = groupImages[messageIdChat] {
  5680. timeMessage.isHidden = true
  5681. statusMessage.isHidden = true
  5682. imageStared.isHidden = true
  5683. topMarginText.constant = topMarginText.constant + 225
  5684. let listImageThumb: [UIImageView] = [UIImageView(), UIImageView(), UIImageView(), UIImageView()]
  5685. for i in 0..<4 {
  5686. containerMessage.addSubview(listImageThumb[i])
  5687. listImageThumb[i].layer.cornerRadius = 5.0
  5688. listImageThumb[i].clipsToBounds = true
  5689. listImageThumb[i].contentMode = .scaleAspectFill
  5690. let widthHeightImage: CGFloat = 120
  5691. switch i {
  5692. case 0:
  5693. listImageThumb[i].anchor(top: containerMessage.topAnchor, left: containerMessage.leftAnchor, paddingTop: 5, paddingLeft: 5, width: widthHeightImage, height: widthHeightImage)
  5694. case 1:
  5695. listImageThumb[i].anchor(top: containerMessage.topAnchor, left: listImageThumb[0].rightAnchor, right: containerMessage.rightAnchor, paddingTop: 5, paddingLeft: 5, paddingRight: 5, width: widthHeightImage, height: widthHeightImage)
  5696. case 2:
  5697. listImageThumb[i].anchor(left: containerMessage.leftAnchor, bottom: containerMessage.bottomAnchor, paddingLeft: 5, paddingBottom: 5, width: widthHeightImage, height: widthHeightImage)
  5698. default:
  5699. listImageThumb[i].anchor(left: listImageThumb[2].rightAnchor, bottom: containerMessage.bottomAnchor, right: containerMessage.rightAnchor, paddingLeft: 5, paddingBottom: 5, paddingRight: 5, width: widthHeightImage, height: widthHeightImage)
  5700. }
  5701. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  5702. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  5703. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  5704. if let dirPath = paths.first {
  5705. let thumbURL = URL(fileURLWithPath: dirPath).appendingPathComponent(listImages[i].thumbId)
  5706. let image : UIImage? = {
  5707. if let img = Nexilis.imageCache.object(forKey: listImages[i].thumbId as NSString) {
  5708. return img
  5709. }
  5710. else if let img = UIImage(contentsOfFile: thumbURL.path)?.resize(target: CGSize(width: 500, height: 500)) {
  5711. Nexilis.imageCache.setObject(img, forKey: listImages[i].thumbId as NSString)
  5712. return img
  5713. }
  5714. return nil
  5715. }()
  5716. // let image = UIGraphicsRenderer.renderImageAt(url: thumbURL as NSURL, size: CGSize(width: 250, height: 250))
  5717. listImageThumb[i].image = image
  5718. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(listImages[i].imageId)
  5719. if !FileManager.default.fileExists(atPath: imageURL.path) && !FileEncryption.shared.isSecureExists(filename: imageURL.lastPathComponent) {
  5720. let blurEffect = UIBlurEffect(style: UIBlurEffect.Style.light)
  5721. let blurEffectView = UIVisualEffectView(effect: blurEffect)
  5722. blurEffectView.frame = CGRect(x: 0, y: 0, width: listImageThumb[i].frame.size.width, height: listImageThumb[i].frame.size.height)
  5723. blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  5724. listImageThumb[i].addSubview(blurEffectView)
  5725. }
  5726. }
  5727. let containerTimeStatus = UIView()
  5728. listImageThumb[i].addSubview(containerTimeStatus)
  5729. containerTimeStatus.anchor(bottom: listImageThumb[i].bottomAnchor, right: listImageThumb[i].rightAnchor, height: 15)
  5730. let widthcontainerTimeStatus = containerTimeStatus.widthAnchor.constraint(equalToConstant: 50)
  5731. widthcontainerTimeStatus.isActive = true
  5732. containerTimeStatus.layer.cornerRadius = 5.0
  5733. containerTimeStatus.layer.masksToBounds = true
  5734. containerTimeStatus.backgroundColor = .black.withAlphaComponent(0.15)
  5735. let timeInImage = UILabel()
  5736. containerTimeStatus.addSubview(timeInImage)
  5737. let date = Date(milliseconds: Int64(listImages[i].time) ?? 100)
  5738. let formatter = DateFormatter()
  5739. formatter.dateFormat = "HH:mm"
  5740. formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
  5741. timeInImage.text = formatter.string(from: date as Date)
  5742. timeInImage.textColor = .white
  5743. timeInImage.font = UIFont.systemFont(ofSize: 10, weight: .medium)
  5744. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  5745. let statusInImage = UIImageView()
  5746. containerTimeStatus.addSubview(statusInImage)
  5747. statusInImage.anchor(right: containerTimeStatus.rightAnchor, centerY: containerTimeStatus.centerYAnchor, width: 15, height: 15)
  5748. if listImages[i].status == "0" {
  5749. statusMessage.image = UIImage(systemName: "xmark.circle")!.withTintColor(UIColor.red, renderingMode: .alwaysOriginal)
  5750. } else if listImages[i].status == "1" {
  5751. statusInImage.image = UIImage(systemName: "clock.arrow.circlepath")!.withTintColor(UIColor.white, renderingMode: .alwaysOriginal)
  5752. } else if listImages[i].status == "2" {
  5753. statusInImage.image = UIImage(named: "checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.white)
  5754. } else if listImages[i].status == "3" {
  5755. statusInImage.image = UIImage(named: "double-checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.white)
  5756. } else {
  5757. statusInImage.image = UIImage(named: "double-checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.systemBlue)
  5758. }
  5759. timeInImage.anchor(right: statusInImage.leftAnchor, centerY: containerTimeStatus.centerYAnchor, height: 15)
  5760. } else {
  5761. timeInImage.anchor(right: containerTimeStatus.rightAnchor, paddingRight: 5, centerY: containerTimeStatus.centerYAnchor, height: 15)
  5762. widthcontainerTimeStatus.constant = widthcontainerTimeStatus.constant - 10
  5763. }
  5764. if listImages[i].dataMessage["is_stared"] as? String == "1" {
  5765. let iconStar = UIImageView()
  5766. containerTimeStatus.addSubview(iconStar)
  5767. iconStar.anchor(right: timeInImage.leftAnchor, paddingRight: 2, centerY: containerTimeStatus.centerYAnchor, width: 15, height: 15)
  5768. widthcontainerTimeStatus.constant = widthcontainerTimeStatus.constant + 15
  5769. iconStar.image = UIImage(systemName: "star.fill")
  5770. iconStar.tintColor = .white
  5771. }
  5772. if !copySession && !forwardSession && !deleteSession {
  5773. let objectTap = ObjectGesture(target: self, action: #selector(imageGroupingTapped(_:)))
  5774. listImageThumb[i].isUserInteractionEnabled = true
  5775. listImageThumb[i].addGestureRecognizer(objectTap)
  5776. objectTap.indexImageTapped = i
  5777. objectTap.listImageFromGrouping = listImages
  5778. objectTap.isInitiator = dataMessages[indexPath.row]["f_pin"] as? String == idMe
  5779. }
  5780. }
  5781. if listImages.count > 4 {
  5782. let blurEffect = UIBlurEffect(style: UIBlurEffect.Style.dark)
  5783. let blurEffectView = UIVisualEffectView(effect: blurEffect)
  5784. blurEffectView.frame = CGRect(x: 0, y: 0, width: listImageThumb[3].frame.size.width, height: listImageThumb[3].frame.size.height)
  5785. blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  5786. listImageThumb[3].addSubview(blurEffectView)
  5787. let countRestImages = UILabel()
  5788. listImageThumb[3].addSubview(countRestImages)
  5789. countRestImages.anchor(centerX: listImageThumb[3].centerXAnchor, centerY: listImageThumb[3].centerYAnchor)
  5790. countRestImages.font = UIFont.systemFont(ofSize: 30, weight: .medium)
  5791. countRestImages.text = "+\(listImages.count - 3)"
  5792. countRestImages.textColor = .white
  5793. }
  5794. } else {
  5795. let getHeightImage: CGFloat = ListGroupImages.getImageSize(image: thumbChat, screenWidth: self.view.frame.size.width * 0.6, screenHeight: 305)!.height
  5796. let getWidthImage: CGFloat = ListGroupImages.getImageSize(image: thumbChat, screenWidth: self.view.frame.size.width * 0.6, screenHeight: 305)!.width
  5797. topMarginText.constant = topMarginText.constant + (getHeightImage < 40 ? 45 : getHeightImage + 5)
  5798. containerMessage.addSubview(imageThumb)
  5799. imageThumb.frame = CGRect(x: 0, y: 0, width: getWidthImage, height: getHeightImage)
  5800. imageThumb.translatesAutoresizingMaskIntoConstraints = false
  5801. let data = queryMessageReply(message_id: reffChat)
  5802. if (reffChat.isEmpty || data.count == 0) && (dataMessages[indexPath.row][TypeDataMessage.is_forwarded] == nil || dataMessages[indexPath.row][TypeDataMessage.is_forwarded] as! Int == 0) {
  5803. imageThumb.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 15).isActive = true
  5804. }
  5805. imageThumb.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
  5806. imageThumb.bottomAnchor.constraint(equalTo: messageText.topAnchor, constant: -5).isActive = true
  5807. imageThumb.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  5808. imageThumb.widthAnchor.constraint(equalToConstant: getWidthImage).isActive = true
  5809. imageThumb.layer.cornerRadius = 5.0
  5810. imageThumb.clipsToBounds = true
  5811. imageThumb.contentMode = .scaleAspectFill
  5812. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  5813. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  5814. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  5815. if let dirPath = paths.first {
  5816. let thumbURL = URL(fileURLWithPath: dirPath).appendingPathComponent(thumbChat)
  5817. DispatchQueue.main.async {
  5818. let image : UIImage? = {
  5819. if let img = Nexilis.imageCache.object(forKey: thumbChat as NSString) {
  5820. return img
  5821. }
  5822. else if let img = UIImage(contentsOfFile: thumbURL.path)?.resize(target: CGSize(width: 500, height: 500)) {
  5823. Nexilis.imageCache.setObject(img, forKey: thumbChat as NSString)
  5824. return img
  5825. }
  5826. return nil
  5827. }()
  5828. imageThumb.image = image
  5829. }
  5830. // let image = UIGraphicsRenderer.renderImageAt(url: thumbURL as NSURL, size: CGSize(width: 250, height: 250))
  5831. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(imageChat)
  5832. if !FileManager.default.fileExists(atPath: imageURL.path) && !FileEncryption.shared.isSecureExists(filename: imageURL.lastPathComponent) {
  5833. let blurEffect = UIBlurEffect(style: UIBlurEffect.Style.light)
  5834. let blurEffectView = UIVisualEffectView(effect: blurEffect)
  5835. blurEffectView.frame = CGRect(x: 0, y: 0, width: imageThumb.frame.size.width, height: imageThumb.frame.size.height)
  5836. blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  5837. imageThumb.addSubview(blurEffectView)
  5838. if !imageChat.isEmpty {
  5839. let imageDownload = UIImageView(image: UIImage(systemName: "arrow.down.circle.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 50, weight: .bold, scale: .default)))
  5840. imageThumb.addSubview(imageDownload)
  5841. imageDownload.tintColor = .black.withAlphaComponent(0.3)
  5842. imageDownload.translatesAutoresizingMaskIntoConstraints = false
  5843. imageDownload.centerXAnchor.constraint(equalTo: imageThumb.centerXAnchor).isActive = true
  5844. imageDownload.centerYAnchor.constraint(equalTo: imageThumb.centerYAnchor).isActive = true
  5845. }
  5846. }
  5847. }
  5848. if (videoChat != "" && gifChat.isEmpty) {
  5849. 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))
  5850. imagePlay.circle()
  5851. imageThumb.addSubview(imagePlay)
  5852. imagePlay.backgroundColor = .black.withAlphaComponent(0.3)
  5853. imagePlay.translatesAutoresizingMaskIntoConstraints = false
  5854. imagePlay.centerXAnchor.constraint(equalTo: imageThumb.centerXAnchor).isActive = true
  5855. imagePlay.centerYAnchor.constraint(equalTo: imageThumb.centerYAnchor).isActive = true
  5856. } else if !gifChat.isEmpty {
  5857. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  5858. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  5859. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  5860. if let dirPath = paths.first {
  5861. let gifURL = URL(fileURLWithPath: dirPath).appendingPathComponent(gifChat)
  5862. if !FileManager.default.fileExists(atPath: gifURL.path) && !FileEncryption.shared.isSecureExists(filename: gifChat) {
  5863. Download().startHTTP(forKey: gifChat, isImage: false) { (name, progress) in
  5864. guard progress == 100 else {
  5865. return
  5866. }
  5867. tableView.reloadRows(at: [indexPath], with: .none)
  5868. }
  5869. } else {
  5870. imageThumb.addSubview(imageGif)
  5871. imageGif.translatesAutoresizingMaskIntoConstraints = false
  5872. imageGif.anchor(top: imageThumb.topAnchor, left: imageThumb.leftAnchor, bottom: imageThumb.bottomAnchor, right: imageThumb.rightAnchor)
  5873. if FileManager.default.fileExists(atPath: gifURL.path) {
  5874. imageGif.image = SDAnimatedImage(contentsOfFile: gifURL.path)
  5875. // imageGif.shouldCustomLoopCount = true
  5876. // imageGif.animationRepeatCount = 4
  5877. } else if FileEncryption.shared.isSecureExists(filename: gifChat){
  5878. do {
  5879. let data = try FileEncryption.shared.readSecure(filename: gifChat)
  5880. if let imageData = SDAnimatedImage(data: data!) {
  5881. imageGif.image = imageData
  5882. // imageGif.shouldCustomLoopCount = true
  5883. // imageGif.animationRepeatCount = 4
  5884. }
  5885. }
  5886. catch {
  5887. print("Error reading secure file")
  5888. }
  5889. }
  5890. }
  5891. }
  5892. }
  5893. if (dataMessages[indexPath.row]["progress"] as! Double != 100.0 && dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  5894. let container = UIView()
  5895. imageThumb.addSubview(container)
  5896. container.translatesAutoresizingMaskIntoConstraints = false
  5897. container.bottomAnchor.constraint(equalTo: imageThumb.bottomAnchor, constant: -10).isActive = true
  5898. container.leadingAnchor.constraint(equalTo: imageThumb.leadingAnchor, constant: 10).isActive = true
  5899. container.widthAnchor.constraint(equalToConstant: 30).isActive = true
  5900. container.heightAnchor.constraint(equalToConstant: 30).isActive = true
  5901. container.backgroundColor = .white.withAlphaComponent(0.1)
  5902. let circlePath = UIBezierPath(arcCenter: CGPoint(x: 10, y: 20), radius: 15, startAngle: -(.pi / 2), endAngle: .pi * 2, clockwise: true)
  5903. let trackShape = CAShapeLayer()
  5904. trackShape.path = circlePath.cgPath
  5905. trackShape.fillColor = UIColor.black.withAlphaComponent(0.3).cgColor
  5906. trackShape.lineWidth = 3
  5907. trackShape.strokeColor = UIColor.blueBubbleColor.withAlphaComponent(0.3).cgColor
  5908. container.backgroundColor = .clear
  5909. container.layer.addSublayer(trackShape)
  5910. let shapeLoading = CAShapeLayer()
  5911. shapeLoading.path = circlePath.cgPath
  5912. shapeLoading.fillColor = UIColor.clear.cgColor
  5913. shapeLoading.lineWidth = 3
  5914. shapeLoading.strokeEnd = 0
  5915. shapeLoading.strokeColor = UIColor.blueBubbleColor.cgColor
  5916. container.layer.addSublayer(shapeLoading)
  5917. let imageupload = UIImageView(image: UIImage(systemName: "arrow.up", withConfiguration: UIImage.SymbolConfiguration(pointSize: 10, weight: .bold, scale: .default)))
  5918. imageupload.tintColor = .white
  5919. container.addSubview(imageupload)
  5920. imageupload.translatesAutoresizingMaskIntoConstraints = false
  5921. imageupload.bottomAnchor.constraint(equalTo: imageThumb.bottomAnchor, constant: -10).isActive = true
  5922. imageupload.leadingAnchor.constraint(equalTo: imageThumb.leadingAnchor, constant: 10).isActive = true
  5923. imageupload.widthAnchor.constraint(equalToConstant: 20).isActive = true
  5924. imageupload.heightAnchor.constraint(equalToConstant: 20).isActive = true
  5925. }
  5926. if !copySession && !forwardSession && !deleteSession {
  5927. let objectTap = ObjectGesture(target: self, action: #selector(contentMessageTapped(_:)))
  5928. imageThumb.isUserInteractionEnabled = true
  5929. imageThumb.addGestureRecognizer(objectTap)
  5930. objectTap.image_id = imageChat
  5931. objectTap.video_id = videoChat
  5932. objectTap.gif_id = gifChat
  5933. objectTap.imageView = imageThumb
  5934. objectTap.indexPath = indexPath
  5935. }
  5936. }
  5937. }
  5938. 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")) {
  5939. topMarginText.constant = topMarginText.constant + 55
  5940. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  5941. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  5942. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  5943. let arrExtFile = (textChat.components(separatedBy: "|")[0]).split(separator: ".")
  5944. let finalExtFile = arrExtFile[arrExtFile.count - 1]
  5945. if let dirPath = paths.first {
  5946. let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(fileChat)
  5947. if FileManager.default.fileExists(atPath: fileURL.path) {
  5948. if let dataFile = try? Data(contentsOf: fileURL) {
  5949. var sizeOfFile = Int(dataFile.count / 1000000)
  5950. if (sizeOfFile < 1) {
  5951. sizeOfFile = Int(dataFile.count / 1000)
  5952. if (finalExtFile.count > 4) {
  5953. messageText.text = "\(sizeOfFile) kB \u{2022} TXT"
  5954. }else {
  5955. messageText.text = "\(sizeOfFile) kB \u{2022} \(finalExtFile.uppercased())"
  5956. }
  5957. } else {
  5958. if (finalExtFile.count > 4) {
  5959. messageText.text = "\(sizeOfFile) MB \u{2022} TXT"
  5960. }else {
  5961. messageText.text = "\(sizeOfFile) MB \u{2022} \(finalExtFile.uppercased())"
  5962. }
  5963. }
  5964. } else {
  5965. messageText.text = ""
  5966. }
  5967. }
  5968. else if FileEncryption.shared.isSecureExists(filename: fileChat) {
  5969. if let dataFile = try? FileEncryption.shared.readSecure(filename: fileChat) {
  5970. var sizeOfFile = Int(dataFile.count / 1000000)
  5971. if (sizeOfFile < 1) {
  5972. sizeOfFile = Int(dataFile.count / 1000)
  5973. if (finalExtFile.count > 4) {
  5974. messageText.text = "\(sizeOfFile) kB \u{2022} TXT"
  5975. }else {
  5976. messageText.text = "\(sizeOfFile) kB \u{2022} \(finalExtFile.uppercased())"
  5977. }
  5978. } else {
  5979. if (finalExtFile.count > 4) {
  5980. messageText.text = "\(sizeOfFile) MB \u{2022} TXT"
  5981. }else {
  5982. messageText.text = "\(sizeOfFile) MB \u{2022} \(finalExtFile.uppercased())"
  5983. }
  5984. }
  5985. } else {
  5986. messageText.text = ""
  5987. }
  5988. }
  5989. }
  5990. containerMessage.addSubview(containerViewFile)
  5991. containerViewFile.translatesAutoresizingMaskIntoConstraints = false
  5992. let data = queryMessageReply(message_id: reffChat)
  5993. if (reffChat.isEmpty || data.count == 0) && (dataMessages[indexPath.row][TypeDataMessage.is_forwarded] == nil || dataMessages[indexPath.row][TypeDataMessage.is_forwarded] as! Int == 0) {
  5994. containerViewFile.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 15).isActive = true
  5995. }
  5996. containerViewFile.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
  5997. containerViewFile.bottomAnchor.constraint(equalTo:messageText.topAnchor, constant: -5).isActive = true
  5998. containerViewFile.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  5999. // containerViewFile.heightAnchor.constraint(equalToConstant: 50).isActive = true
  6000. containerViewFile.backgroundColor = .black.withAlphaComponent(0.2)
  6001. containerViewFile.layer.cornerRadius = 5.0
  6002. containerViewFile.clipsToBounds = true
  6003. let imageFile = UIImageView(image: UIImage(systemName: "doc.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 30, weight: .bold, scale: .default)))
  6004. containerViewFile.addSubview(imageFile)
  6005. let nameFile = UILabel()
  6006. containerViewFile.addSubview(nameFile)
  6007. imageFile.translatesAutoresizingMaskIntoConstraints = false
  6008. imageFile.leadingAnchor.constraint(equalTo: containerViewFile.leadingAnchor, constant: 5).isActive = true
  6009. imageFile.trailingAnchor.constraint(equalTo: nameFile.leadingAnchor, constant: -5).isActive = true
  6010. imageFile.centerYAnchor.constraint(equalTo: containerViewFile.centerYAnchor).isActive = true
  6011. imageFile.widthAnchor.constraint(equalToConstant: 30).isActive = true
  6012. imageFile.heightAnchor.constraint(equalToConstant: 30).isActive = true
  6013. imageFile.tintColor = .docColor
  6014. nameFile.translatesAutoresizingMaskIntoConstraints = false
  6015. nameFile.centerYAnchor.constraint(equalTo: containerViewFile.centerYAnchor).isActive = true
  6016. nameFile.widthAnchor.constraint(lessThanOrEqualToConstant: 200).isActive = true
  6017. nameFile.font = UIFont.systemFont(ofSize: 12, weight: .medium)
  6018. nameFile.textColor = .white
  6019. nameFile.text = textChat.components(separatedBy: "|")[0]
  6020. if (dataMessages[indexPath.row]["progress"] as! Double != 100.0) {
  6021. let containerLoading = UIView()
  6022. containerViewFile.addSubview(containerLoading)
  6023. containerLoading.translatesAutoresizingMaskIntoConstraints = false
  6024. containerLoading.centerYAnchor.constraint(equalTo: containerViewFile.centerYAnchor).isActive = true
  6025. containerLoading.leadingAnchor.constraint(equalTo: nameFile.trailingAnchor, constant: 5).isActive = true
  6026. containerLoading.trailingAnchor.constraint(equalTo: containerViewFile.trailingAnchor, constant: -5).isActive = true
  6027. containerLoading.widthAnchor.constraint(equalToConstant: 30).isActive = true
  6028. containerLoading.heightAnchor.constraint(equalToConstant: 30).isActive = true
  6029. let circlePath = UIBezierPath(arcCenter: CGPoint(x: 15, y: 15), radius: 10, startAngle: -(.pi / 2), endAngle: .pi * 2, clockwise: true)
  6030. let trackShape = CAShapeLayer()
  6031. trackShape.path = circlePath.cgPath
  6032. trackShape.fillColor = UIColor.clear.cgColor
  6033. trackShape.lineWidth = 5
  6034. trackShape.strokeColor = UIColor.blueBubbleColor.withAlphaComponent(0.3).cgColor
  6035. containerLoading.layer.addSublayer(trackShape)
  6036. let shapeLoading = CAShapeLayer()
  6037. shapeLoading.path = circlePath.cgPath
  6038. shapeLoading.fillColor = UIColor.clear.cgColor
  6039. shapeLoading.lineWidth = 3
  6040. shapeLoading.strokeEnd = 0
  6041. shapeLoading.strokeColor = UIColor.secondaryColor.cgColor
  6042. containerLoading.layer.addSublayer(shapeLoading)
  6043. var imageupload = UIImageView(image: UIImage(systemName: "arrow.up", withConfiguration: UIImage.SymbolConfiguration(pointSize: 10, weight: .bold, scale: .default)))
  6044. if dataMessages[indexPath.row]["f_pin"] as? String != idMe {
  6045. imageupload = UIImageView(image: UIImage(systemName: "arrow.down", withConfiguration: UIImage.SymbolConfiguration(pointSize: 10, weight: .bold, scale: .default)))
  6046. shapeLoading.strokeColor = UIColor.blueBubbleColor.cgColor
  6047. }
  6048. imageupload.tintColor = .white
  6049. containerLoading.addSubview(imageupload)
  6050. imageupload.translatesAutoresizingMaskIntoConstraints = false
  6051. imageupload.centerYAnchor.constraint(equalTo: containerLoading.centerYAnchor).isActive = true
  6052. imageupload.centerXAnchor.constraint(equalTo: containerLoading.centerXAnchor).isActive = true
  6053. } else {
  6054. nameFile.trailingAnchor.constraint(equalTo: containerViewFile.trailingAnchor, constant: -5).isActive = true
  6055. }
  6056. if !copySession && !forwardSession && !deleteSession {
  6057. let objectTap = ObjectGesture(target: self, action: #selector(contentMessageTapped(_:)))
  6058. containerViewFile.addGestureRecognizer(objectTap)
  6059. objectTap.containerFile = containerViewFile
  6060. objectTap.labelFile = nameFile
  6061. objectTap.file_id = fileChat
  6062. objectTap.indexPath = indexPath
  6063. }
  6064. }
  6065. let containerLinkMessage = UIView()
  6066. var isLoadingShowLink = false
  6067. if thumbChat.isEmpty && fileChat.isEmpty && !textChat.isEmpty {
  6068. var text = ""
  6069. let listTextSplitBreak = textChat.components(separatedBy: "\n")
  6070. let indexFirstLinkSplitBreak = listTextSplitBreak.firstIndex(where: { $0.contains("www.") || $0.contains("http://") || $0.contains("https://") })
  6071. if indexFirstLinkSplitBreak != nil {
  6072. let listTextSplitSpace = listTextSplitBreak[indexFirstLinkSplitBreak!].components(separatedBy: " ")
  6073. 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) })
  6074. if indexFirstLinkSplitSpace != nil {
  6075. text = listTextSplitSpace[indexFirstLinkSplitSpace!]
  6076. }
  6077. }
  6078. if !text.isEmpty {
  6079. isLoadingShowLink = true
  6080. func showLink() {
  6081. if let data = try! JSONSerialization.jsonObject(with: dataURL.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
  6082. let title = data["title"] as? String ?? ""
  6083. let description = data["description"] as? String ?? ""
  6084. let imageUrl = data["imageUrl"] as? String
  6085. let link = data["link"] as? String ?? ""
  6086. topMarginText.constant = topMarginText.constant + 85
  6087. containerMessage.addSubview(containerLinkMessage)
  6088. containerLinkMessage.translatesAutoresizingMaskIntoConstraints = false
  6089. containerLinkMessage.leadingAnchor.constraint(equalTo:containerMessage.leadingAnchor, constant: 15).isActive = true
  6090. containerLinkMessage.bottomAnchor.constraint(equalTo: messageText.topAnchor, constant: -5).isActive = true
  6091. containerLinkMessage.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  6092. containerLinkMessage.heightAnchor.constraint(equalToConstant: 80.0).isActive = true
  6093. containerLinkMessage.backgroundColor = .gray.withAlphaComponent(0.2)
  6094. let imagePreview = UIImageView()
  6095. if imageUrl != nil {
  6096. containerLinkMessage.addSubview(imagePreview)
  6097. imagePreview.translatesAutoresizingMaskIntoConstraints = false
  6098. imagePreview.leadingAnchor.constraint(equalTo: containerLinkMessage.leadingAnchor).isActive = true
  6099. imagePreview.bottomAnchor.constraint(equalTo: containerLinkMessage.bottomAnchor).isActive = true
  6100. imagePreview.topAnchor.constraint(equalTo: containerLinkMessage.topAnchor).isActive = true
  6101. imagePreview.widthAnchor.constraint(equalToConstant: 80.0).isActive = true
  6102. imagePreview.loadImageAsync(with: imageUrl)
  6103. imagePreview.contentMode = .scaleToFill
  6104. }
  6105. let titlePreview = UILabel()
  6106. containerLinkMessage.addSubview(titlePreview)
  6107. titlePreview.translatesAutoresizingMaskIntoConstraints = false
  6108. if imageUrl != nil {
  6109. titlePreview.leadingAnchor.constraint(equalTo: imagePreview.trailingAnchor, constant: 5.0).isActive = true
  6110. } else {
  6111. titlePreview.leadingAnchor.constraint(equalTo: containerLinkMessage.leadingAnchor, constant: 5.0).isActive = true
  6112. }
  6113. titlePreview.topAnchor.constraint(equalTo: containerLinkMessage.topAnchor, constant: 10.0).isActive = true
  6114. titlePreview.trailingAnchor.constraint(equalTo: containerLinkMessage.trailingAnchor, constant: -5.0).isActive = true
  6115. titlePreview.text = title
  6116. titlePreview.font = UIFont.systemFont(ofSize: 14.0, weight: .bold)
  6117. titlePreview.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
  6118. let descPreview = UILabel()
  6119. containerLinkMessage.addSubview(descPreview)
  6120. descPreview.translatesAutoresizingMaskIntoConstraints = false
  6121. if imageUrl != nil {
  6122. descPreview.leadingAnchor.constraint(equalTo: imagePreview.trailingAnchor, constant: 5.0).isActive = true
  6123. } else {
  6124. descPreview.leadingAnchor.constraint(equalTo: containerLinkMessage.leadingAnchor, constant: 5.0).isActive = true
  6125. }
  6126. descPreview.topAnchor.constraint(equalTo: titlePreview.bottomAnchor).isActive = true
  6127. descPreview.trailingAnchor.constraint(equalTo: containerLinkMessage.trailingAnchor, constant: -5.0).isActive = true
  6128. descPreview.text = description
  6129. descPreview.font = UIFont.systemFont(ofSize: 12.0)
  6130. descPreview.textColor = .gray
  6131. descPreview.numberOfLines = 1
  6132. let linkPreview = UILabel()
  6133. containerLinkMessage.addSubview(linkPreview)
  6134. linkPreview.translatesAutoresizingMaskIntoConstraints = false
  6135. if imageUrl != nil {
  6136. linkPreview.leadingAnchor.constraint(equalTo: imagePreview.trailingAnchor, constant: 5.0).isActive = true
  6137. } else {
  6138. linkPreview.leadingAnchor.constraint(equalTo: containerLinkMessage.leadingAnchor, constant: 5.0).isActive = true
  6139. }
  6140. linkPreview.topAnchor.constraint(equalTo: descPreview.bottomAnchor, constant: 8.0).isActive = true
  6141. linkPreview.trailingAnchor.constraint(equalTo: containerLinkMessage.trailingAnchor, constant: -5.0).isActive = true
  6142. linkPreview.text = link
  6143. linkPreview.font = UIFont.systemFont(ofSize: 10.0)
  6144. linkPreview.textColor = .gray
  6145. linkPreview.numberOfLines = 1
  6146. if dataMessages[indexPath.row][TypeDataMessage.is_forwarded] != nil && dataMessages[indexPath.row][TypeDataMessage.is_forwarded] as! Int != 0 {
  6147. showForwardedSign()
  6148. }
  6149. if !copySession && !forwardSession && !deleteSession {
  6150. let objectTap = ObjectGesture(target: self, action: #selector(tapMessageText(_:)))
  6151. objectTap.message_id = text
  6152. containerLinkMessage.addGestureRecognizer(objectTap)
  6153. }
  6154. }
  6155. }
  6156. var dataURL = ""
  6157. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  6158. do {
  6159. if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "select data_link from LINK_PREVIEW where link='\(text)'"), cursor.next() {
  6160. if let data = cursor.string(forColumnIndex: 0) {
  6161. dataURL = data
  6162. }
  6163. cursor.close()
  6164. }
  6165. } catch {
  6166. rollback.pointee = true
  6167. print("Access database error: \(error.localizedDescription)")
  6168. }
  6169. })
  6170. if dataURL.isEmpty {
  6171. let urlConfig = URLSessionConfiguration.default
  6172. let sessionDelegate = SelfSignedURLSessionDelegate()
  6173. let session = URLSession(configuration: urlConfig, delegate: sessionDelegate, delegateQueue: nil)
  6174. let slp = SwiftLinkPreview(session: session,
  6175. workQueue: SwiftLinkPreview.defaultWorkQueue,
  6176. responseQueue: DispatchQueue.main,
  6177. cache: DisabledCache.instance)
  6178. let preview = slp.preview(text,
  6179. onSuccess: { result in
  6180. let title = result.title ?? "No Title"
  6181. let description = text.contains("google.com") ? "" : result.description
  6182. let imageUrl = result.icon
  6183. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  6184. do {
  6185. var dataJson: [String: Any] = [:]
  6186. dataJson["title"] = title
  6187. dataJson["description"] = description
  6188. dataJson["imageUrl"] = imageUrl
  6189. dataJson["link"] = text
  6190. guard let json = String(data: try! JSONSerialization.data(withJSONObject: dataJson, options: []), encoding: String.Encoding.utf8) else {
  6191. return
  6192. }
  6193. _ = try Database.shared.insertRecord(fmdb: fmdb, table: "LINK_PREVIEW", cvalues: [
  6194. "id" : "\(Date().currentTimeMillis().toHex())",
  6195. "link" : text,
  6196. "data_link" : json,
  6197. "retry": 0
  6198. ], replace: true)
  6199. dataURL = json
  6200. showLink()
  6201. DispatchQueue.main.async {
  6202. tableView.reloadRows(at: [indexPath], with: .none)
  6203. }
  6204. } catch {
  6205. rollback.pointee = true
  6206. print("Access database error: \(error.localizedDescription)")
  6207. }
  6208. })
  6209. }, onError: { error in
  6210. })
  6211. } else {
  6212. showLink()
  6213. }
  6214. }
  6215. }
  6216. if (reffChat != "" && dataMessages[indexPath.row]["message_scope_id"] as? String ?? "" != "18") {
  6217. let data = queryMessageReply(message_id: reffChat)
  6218. if data.count != 0 {
  6219. topMarginText.constant = topMarginText.constant + 55
  6220. let containerReply = UIView()
  6221. containerMessage.addSubview(containerReply)
  6222. containerReply.translatesAutoresizingMaskIntoConstraints = false
  6223. containerReply.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
  6224. containerReply.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 15).isActive = true
  6225. if thumbChat != "" && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as? String ?? "" != "1") {
  6226. containerReply.bottomAnchor.constraint(equalTo: imageThumb.topAnchor, constant: -5).isActive = true
  6227. } else if fileChat != "" && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as? String ?? "" != "1") {
  6228. containerReply.bottomAnchor.constraint(equalTo: containerViewFile.topAnchor, constant: -5).isActive = true
  6229. } else if containerMessage.subviews.contains(containerLinkMessage) {
  6230. containerReply.bottomAnchor.constraint(equalTo: containerLinkMessage.topAnchor, constant: -5).isActive = true
  6231. } else if dataMessages[indexPath.row]["attachment_flag"] as? String == "11" && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as? String ?? "" != "1") {
  6232. containerReply.bottomAnchor.constraint(equalTo: imageSticker.topAnchor, constant: -5).isActive = true
  6233. } else {
  6234. containerReply.bottomAnchor.constraint(equalTo: messageText.topAnchor, constant: -5).isActive = true
  6235. }
  6236. containerReply.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  6237. containerReply.heightAnchor.constraint(equalToConstant: 50).isActive = true
  6238. containerReply.backgroundColor = .black.withAlphaComponent(0.2)
  6239. containerReply.layer.cornerRadius = 5
  6240. containerReply.clipsToBounds = true
  6241. let leftReply = UIView()
  6242. containerReply.addSubview(leftReply)
  6243. leftReply.translatesAutoresizingMaskIntoConstraints = false
  6244. leftReply.leadingAnchor.constraint(equalTo: containerReply.leadingAnchor).isActive = true
  6245. leftReply.topAnchor.constraint(equalTo: containerReply.topAnchor).isActive = true
  6246. leftReply.bottomAnchor.constraint(equalTo: containerReply.bottomAnchor).isActive = true
  6247. leftReply.widthAnchor.constraint(equalToConstant: 3).isActive = true
  6248. leftReply.layer.cornerRadius = 5
  6249. leftReply.clipsToBounds = true
  6250. leftReply.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMinXMinYCorner]
  6251. let titleReply = UILabel()
  6252. containerReply.addSubview(titleReply)
  6253. titleReply.translatesAutoresizingMaskIntoConstraints = false
  6254. titleReply.leadingAnchor.constraint(equalTo: leftReply.leadingAnchor, constant: 10).isActive = true
  6255. titleReply.topAnchor.constraint(equalTo: containerReply.topAnchor, constant: 10).isActive = true
  6256. titleReply.trailingAnchor.constraint(lessThanOrEqualTo: containerReply.trailingAnchor, constant: -20).isActive = true
  6257. titleReply.font = UIFont.systemFont(ofSize: 12).bold
  6258. if (data["f_pin"] as? String == idMe) {
  6259. titleReply.text = "You".localized()
  6260. if dataMessages[indexPath.row]["f_pin"] as? String == idMe {
  6261. titleReply.textColor = .white
  6262. leftReply.backgroundColor = .white
  6263. } else {
  6264. titleReply.textColor = .mainColor
  6265. leftReply.backgroundColor = .mainColor
  6266. }
  6267. } else {
  6268. if isContactCenter {
  6269. let user: [User] = users.filter({$0.pin == data["f_pin"] as? String})
  6270. titleReply.text = user.first!.fullName
  6271. } else {
  6272. titleReply.text = self.dataPerson["name"]!!
  6273. }
  6274. if dataMessages[indexPath.row]["f_pin"] as? String == idMe {
  6275. titleReply.textColor = .white
  6276. leftReply.backgroundColor = .white
  6277. } else {
  6278. titleReply.textColor = .mainColor
  6279. leftReply.backgroundColor = .mainColor
  6280. }
  6281. }
  6282. let contentReply = UILabel()
  6283. containerReply.addSubview(contentReply)
  6284. contentReply.translatesAutoresizingMaskIntoConstraints = false
  6285. contentReply.leadingAnchor.constraint(equalTo: leftReply.leadingAnchor, constant: 10).isActive = true
  6286. contentReply.bottomAnchor.constraint(equalTo: containerReply.bottomAnchor, constant: -10).isActive = true
  6287. contentReply.font = UIFont.systemFont(ofSize: 10)
  6288. let message_text = data["message_text"] as? String ?? ""
  6289. let attachment_flag = data["attachment_flag"] as? String ?? ""
  6290. let thumb_chat = data["thumb_id"] as? String ?? ""
  6291. let image_chat = data["image_id"] as? String ?? ""
  6292. let video_chat = data["video_id"] as? String ?? ""
  6293. let file_chat = data["file_id"] as? String ?? ""
  6294. if (attachment_flag == "0" && thumb_chat == "") {
  6295. contentReply.trailingAnchor.constraint(equalTo: containerReply.trailingAnchor, constant: -20).isActive = true
  6296. contentReply.attributedText = message_text.richText()
  6297. } else if (attachment_flag == "1" || image_chat != "") {
  6298. if (message_text == "") {
  6299. contentReply.text = "📷 Photo".localized()
  6300. } else {
  6301. contentReply.attributedText = message_text.richText()
  6302. }
  6303. } else if (attachment_flag == "2" || video_chat != "") {
  6304. if (message_text == "") {
  6305. contentReply.text = "📹 Video".localized()
  6306. } else {
  6307. contentReply.attributedText = message_text.richText()
  6308. }
  6309. } else if (attachment_flag == "6" || file_chat != ""){
  6310. contentReply.trailingAnchor.constraint(equalTo: containerReply.trailingAnchor, constant: -20).isActive = true
  6311. contentReply.text = "📄 \(message_text.components(separatedBy: "|")[0])"
  6312. } else if (attachment_flag == "11") {
  6313. contentReply.text = "❤️ Sticker"
  6314. }
  6315. contentReply.textColor = .white.withAlphaComponent(0.8)
  6316. if (attachment_flag == "1" || attachment_flag == "2" || image_chat != "" || video_chat != "") {
  6317. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  6318. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  6319. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  6320. if let dirPath = paths.first {
  6321. let thumbURL = URL(fileURLWithPath: dirPath).appendingPathComponent(thumb_chat)
  6322. DispatchQueue.main.async {
  6323. let image : UIImage? = {
  6324. if let img = Nexilis.imageCache.object(forKey: thumbChat as NSString) {
  6325. return img
  6326. }
  6327. else if let img = UIImage(contentsOfFile: thumbURL.path)?.resize(target: CGSize(width: 500, height: 500)) {
  6328. Nexilis.imageCache.setObject(img, forKey: thumbChat as NSString)
  6329. return img
  6330. }
  6331. return nil
  6332. }()
  6333. // let image = UIGraphicsRenderer.renderImageAt(url: thumbURL as NSURL, size: CGSize(width: 250, height: 250))
  6334. let imageThumb = UIImageView(image: image)
  6335. containerReply.addSubview(imageThumb)
  6336. imageThumb.layer.cornerRadius = 2.0
  6337. imageThumb.clipsToBounds = true
  6338. imageThumb.contentMode = .scaleAspectFill
  6339. imageThumb.translatesAutoresizingMaskIntoConstraints = false
  6340. imageThumb.trailingAnchor.constraint(equalTo: containerReply.trailingAnchor, constant: -10).isActive = true
  6341. imageThumb.centerYAnchor.constraint(equalTo: containerReply.centerYAnchor).isActive = true
  6342. imageThumb.widthAnchor.constraint(equalToConstant: 30).isActive = true
  6343. imageThumb.heightAnchor.constraint(equalToConstant: 30).isActive = true
  6344. if (attachment_flag == "2") {
  6345. let imagePlay = UIImageView(image: UIImage(systemName: "play.circle.fill"))
  6346. imageThumb.addSubview(imagePlay)
  6347. imagePlay.clipsToBounds = true
  6348. imagePlay.translatesAutoresizingMaskIntoConstraints = false
  6349. imagePlay.centerYAnchor.constraint(equalTo: imageThumb.centerYAnchor).isActive = true
  6350. imagePlay.centerXAnchor.constraint(equalTo: imageThumb.centerXAnchor).isActive = true
  6351. imagePlay.widthAnchor.constraint(equalToConstant: 10).isActive = true
  6352. imagePlay.heightAnchor.constraint(equalToConstant: 10).isActive = true
  6353. imagePlay.tintColor = .white
  6354. }
  6355. titleReply.trailingAnchor.constraint(equalTo: imageThumb.leadingAnchor, constant: -20).isActive = true
  6356. contentReply.trailingAnchor.constraint(equalTo: imageThumb.leadingAnchor, constant: -20).isActive = true
  6357. }
  6358. }
  6359. }
  6360. if (attachment_flag == "11" && message_text.components(separatedBy: "/").count > 1) {
  6361. let imageSticker = UIImageView(image: UIImage(named: (message_text.components(separatedBy: "/")[1]), in: Bundle.resourceBundle(for: Nexilis.self), with: nil))
  6362. containerReply.addSubview(imageSticker)
  6363. imageSticker.layer.cornerRadius = 2.0
  6364. imageSticker.clipsToBounds = true
  6365. imageSticker.translatesAutoresizingMaskIntoConstraints = false
  6366. imageSticker.trailingAnchor.constraint(equalTo: containerReply.trailingAnchor, constant: -10).isActive = true
  6367. imageSticker.centerYAnchor.constraint(equalTo: containerReply.centerYAnchor).isActive = true
  6368. imageSticker.widthAnchor.constraint(equalToConstant: 30).isActive = true
  6369. imageSticker.heightAnchor.constraint(equalToConstant: 30).isActive = true
  6370. titleReply.trailingAnchor.constraint(equalTo: imageSticker.leadingAnchor, constant: -20).isActive = true
  6371. contentReply.trailingAnchor.constraint(equalTo: imageSticker.leadingAnchor, constant: -20).isActive = true
  6372. }
  6373. if !copySession && !forwardSession && !deleteSession {
  6374. let objectTap = ObjectGesture(target: self, action: #selector(contentMessageTapped(_:)))
  6375. containerReply.addGestureRecognizer(objectTap)
  6376. objectTap.indexPath = indexPath
  6377. objectTap.message_id = data["message_id"] as? String ?? ""
  6378. }
  6379. }
  6380. }
  6381. if dataMessages[indexPath.row][TypeDataMessage.is_forwarded] != nil && dataMessages[indexPath.row][TypeDataMessage.is_forwarded] as! Int != 0 && !isLoadingShowLink {
  6382. showForwardedSign()
  6383. }
  6384. func showForwardedSign() {
  6385. topMarginText.constant = topMarginText.constant + 20
  6386. let containerForwarded = UIView()
  6387. containerMessage.addSubview(containerForwarded)
  6388. containerForwarded.translatesAutoresizingMaskIntoConstraints = false
  6389. containerForwarded.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
  6390. containerForwarded.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 15).isActive = true
  6391. containerForwarded.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
  6392. containerForwarded.heightAnchor.constraint(equalToConstant: 20).isActive = true
  6393. if thumbChat != "" && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as? String ?? "" != "1") {
  6394. containerForwarded.bottomAnchor.constraint(equalTo: imageThumb.topAnchor, constant: -5).isActive = true
  6395. } else if fileChat != "" && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as? String ?? "" != "1") {
  6396. containerForwarded.bottomAnchor.constraint(equalTo: containerViewFile.topAnchor, constant: -5).isActive = true
  6397. } else if containerMessage.subviews.contains(containerLinkMessage) {
  6398. containerForwarded.bottomAnchor.constraint(equalTo: containerLinkMessage.topAnchor, constant: -5).isActive = true
  6399. } else if dataMessages[indexPath.row]["attachment_flag"] as? String == "11" && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as? String ?? "" != "1") {
  6400. containerForwarded.bottomAnchor.constraint(equalTo: imageSticker.topAnchor, constant: -5).isActive = true
  6401. }
  6402. let imageForwarded = UIImageView()
  6403. containerForwarded.addSubview(imageForwarded)
  6404. imageForwarded.anchor(top: containerForwarded.topAnchor, left: containerForwarded.leftAnchor, width: 15, height: 15)
  6405. imageForwarded.image = UIImage(systemName: "arrowshape.turn.up.right.fill")
  6406. imageForwarded.tintColor = .gray
  6407. let titleForwarded = UILabel()
  6408. containerForwarded.addSubview(titleForwarded)
  6409. titleForwarded.anchor(top: containerForwarded.topAnchor, left: imageForwarded.rightAnchor, right: containerForwarded.rightAnchor, height: 15)
  6410. titleForwarded.font = .systemFont(ofSize: 15)
  6411. let textForwarded = "Forwarded".localized()
  6412. titleForwarded.attributedText = " %\(textForwarded)%".richText()
  6413. }
  6414. topMarginText.isActive = true
  6415. // let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(panGestureCellAction))
  6416. // panGestureRecognizer.delegate = self
  6417. // cellMessage.addGestureRecognizer(panGestureRecognizer)
  6418. return cell
  6419. }
  6420. @objc func imageGroupingTapped(_ sender: ObjectGesture) {
  6421. let listGroupingImages = ListGroupImages()
  6422. listGroupingImages.imageTapped = sender.indexImageTapped
  6423. listGroupingImages.listGroupingImages = sender.listImageFromGrouping
  6424. listGroupingImages.titleName = titleText
  6425. listGroupingImages.isInitiator = sender.isInitiator
  6426. listGroupingImages.updateEditor = { [self] updatedData, replyData, isUpdateDelete in
  6427. if replyData.count == 0 {
  6428. if updatedData.count != 0 && !isUpdateDelete {
  6429. groupImages[sender.listImageFromGrouping[0].messageId] = updatedData
  6430. } else if updatedData.count > 0 {
  6431. let deletedForEveryoneData = updatedData.filter({ $0.dataMessage["lock"] as? String == "1" })
  6432. if deletedForEveryoneData.count != 0 {
  6433. if groupImages[sender.listImageFromGrouping[0].messageId] != nil {
  6434. var dataWillEmpty = updatedData
  6435. while dataWillEmpty.count > 0 {
  6436. if let lastIdx = dataWillEmpty.lastIndex(where: { $0.dataMessage["lock"] as? String == "1" }) {
  6437. if let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String ?? "" == sender.listImageFromGrouping[0].messageId }) {
  6438. if dataWillEmpty[lastIdx].messageId == sender.listImageFromGrouping[0].messageId {
  6439. self.dataMessages.remove(at: idx)
  6440. self.dataMessages.insert(dataWillEmpty[lastIdx].dataMessage, at: idx)
  6441. } else {
  6442. self.dataMessages.insert(dataWillEmpty[lastIdx].dataMessage, at: idx + 1)
  6443. }
  6444. let subData = Array(updatedData[lastIdx+1..<dataWillEmpty.count])
  6445. if subData.count >= 4 {
  6446. groupImages[subData[0].messageId] = subData
  6447. self.dataMessages.insert(subData[0].dataMessage, at: lastIdx + 1)
  6448. } else {
  6449. if subData.count > 0 {
  6450. self.dataMessages.insert(contentsOf: subData.map({ $0.dataMessage }), at: idx + (dataWillEmpty[lastIdx].messageId == sender.listImageFromGrouping[0].messageId ? 1 : 2))
  6451. }
  6452. }
  6453. }
  6454. dataWillEmpty.removeSubrange(lastIdx..<dataWillEmpty.count)
  6455. } else if dataWillEmpty.count >= 4 {
  6456. groupImages[dataWillEmpty[0].messageId] = dataWillEmpty
  6457. dataWillEmpty.removeAll()
  6458. } else {
  6459. if let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String ?? "" == sender.listImageFromGrouping[0].messageId }) {
  6460. self.dataMessages.remove(at: idx)
  6461. self.dataMessages.insert(contentsOf: dataWillEmpty.map({ $0.dataMessage }), at: idx)
  6462. groupImages.removeValue(forKey: sender.listImageFromGrouping[0].messageId)
  6463. }
  6464. dataWillEmpty.removeAll()
  6465. }
  6466. }
  6467. } else {
  6468. }
  6469. } else {
  6470. if updatedData.count >= 4 {
  6471. if updatedData[0].messageId == sender.listImageFromGrouping[0].messageId {
  6472. groupImages[sender.listImageFromGrouping[0].messageId] = updatedData
  6473. } else {
  6474. if let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String ?? "" == sender.listImageFromGrouping[0].messageId }) {
  6475. self.dataMessages.remove(at: idx)
  6476. self.dataMessages.insert(updatedData[0].dataMessage, at: idx)
  6477. groupImages.removeValue(forKey: sender.listImageFromGrouping[0].messageId)
  6478. groupImages[updatedData[0].messageId] = updatedData
  6479. }
  6480. }
  6481. } else {
  6482. if let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String ?? "" == sender.listImageFromGrouping[0].messageId }) {
  6483. groupImages.removeValue(forKey: sender.listImageFromGrouping[0].messageId)
  6484. self.dataMessages.remove(at: idx)
  6485. let dataMessageInGrouping = updatedData.map({ $0.dataMessage })
  6486. self.dataMessages.insert(contentsOf: dataMessageInGrouping, at: idx)
  6487. }
  6488. }
  6489. }
  6490. } else {
  6491. groupImages.removeValue(forKey: sender.listImageFromGrouping[0].messageId)
  6492. if let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String ?? "" == sender.listImageFromGrouping[0].messageId }) {
  6493. self.dataMessages.remove(at: idx)
  6494. }
  6495. }
  6496. DispatchQueue.main.async { [self] in
  6497. tableChatView.reloadData()
  6498. }
  6499. } else if replyData.count != 0 {
  6500. handleReply(indexPath: IndexPath(row: 0, section: 0), dataMessagesImage: replyData)
  6501. }
  6502. }
  6503. self.navigationController?.pushViewController(listGroupingImages, animated: true)
  6504. }
  6505. @objc func tapAck(_ sender: ObjectGesture) {
  6506. if blocking == "1" {
  6507. self.view.makeToast("You blocked this user".localized(), duration: 3)
  6508. return
  6509. }
  6510. if blocking == "-1" {
  6511. self.view.makeToast("You have been blocked by this user".localized(), duration: 3)
  6512. return
  6513. }
  6514. let indexPath = sender.indexPath
  6515. let dataMessages = self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == dataDates[indexPath.section]})
  6516. if dataMessages[indexPath.row]["status"] as? String ?? "" == "8" {
  6517. return
  6518. }
  6519. if !CheckConnection.isConnectedToNetwork() || API.nGetCLXConnState() == 0 {
  6520. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  6521. imageView.tintColor = .white
  6522. 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)
  6523. banner.show()
  6524. return
  6525. }
  6526. DispatchQueue.global().async {
  6527. 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: ""))
  6528. if result != nil {
  6529. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  6530. do {
  6531. _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
  6532. "status" : "8"
  6533. ], _where: "message_id = '\(dataMessages[indexPath.row]["message_id"] as? String ?? "")'")
  6534. } catch {
  6535. rollback.pointee = true
  6536. print("Access database error: \(error.localizedDescription)")
  6537. }
  6538. })
  6539. DispatchQueue.main.async {
  6540. if let index = self.dataMessages.firstIndex(where: {$0["message_id"] as? String == dataMessages[indexPath.row]["message_id"] as? String}) {
  6541. self.dataMessages[index]["status"] = "8"
  6542. let section = self.dataDates.firstIndex(of: self.dataMessages[index]["chat_date"] as? String ?? "")
  6543. 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 ?? ""})
  6544. if row != nil && section != nil {
  6545. self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
  6546. }
  6547. self.view.makeToast("Confirmation Success.".localized(), duration: 3)
  6548. }
  6549. }
  6550. }
  6551. }
  6552. }
  6553. // public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
  6554. // let velocity : CGPoint = gestureRecognizer.location(in: tableChatView)
  6555. // if velocity.x < 0 {
  6556. // return false
  6557. // }
  6558. // return abs(Float(velocity.x)) > abs(Float(velocity.y))
  6559. // }
  6560. //
  6561. // @objc func panGestureCellAction(recognizer: UIPanGestureRecognizer) {
  6562. // let translation = recognizer.translation(in: tableChatView)
  6563. // let x = recognizer.view?.frame.origin.x ?? 0
  6564. // if x >= -(recognizer.view?.frame.size.width ?? 0) * 0.05 {
  6565. // recognizer.view?.center = CGPoint(
  6566. // x: (recognizer.view?.center.x ?? 0) + translation.x,
  6567. // y: (recognizer.view?.center.y ?? 0))
  6568. // recognizer.setTranslation(CGPoint(x: 0, y: 0), in: view)
  6569. // if (recognizer.view?.frame.origin.x ?? 0) > UIScreen.main.bounds.size.width * 0.9 {
  6570. // UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseOut, animations: {
  6571. // 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)
  6572. // })
  6573. // }
  6574. // }
  6575. // if x <= -(recognizer.view?.frame.size.width ?? 0) * 0.05 {
  6576. // let idMe = User.getMyPin() as String?
  6577. // let indexPath = self.tableChatView.indexPath(for: recognizer.view! as! UITableViewCell)
  6578. // let dataMessages = self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == dataDates[indexPath!.section]})
  6579. // if (dataMessages[indexPath!.row]["f_pin"] as? String == idMe) {
  6580. // let messageInfoVC = MessageInfo()
  6581. // messageInfoVC.data = dataMessages[indexPath!.row]
  6582. // self.navigationController?.pushViewController(messageInfoVC, animated: true)
  6583. // return
  6584. // }
  6585. // }
  6586. // if x >= ((recognizer.view?.frame.size.width ?? 0) * 0.2) {
  6587. // if !hapticSwipeLeft {
  6588. // UINotificationFeedbackGenerator().notificationOccurred(.success)
  6589. // }
  6590. // hapticSwipeLeft = true
  6591. // } else if x < ((recognizer.view?.frame.size.width ?? 0) * 0.2) {
  6592. // hapticSwipeLeft = false
  6593. // }
  6594. // if recognizer.state == .ended {
  6595. // UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseOut) {
  6596. // 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)
  6597. // } completion: { (finished) in
  6598. // if x > ((recognizer.view?.frame.size.width ?? 0) * 0.2) {
  6599. // self.hapticSwipeLeft = false
  6600. //
  6601. // }
  6602. // }
  6603. // }
  6604. // }
  6605. public func numberOfSections(in tableView: UITableView) -> Int {
  6606. dataDates.count
  6607. }
  6608. public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  6609. let count = dataMessages.filter({ $0["chat_date"] as? String ?? "" == dataDates[section] }).count
  6610. return count
  6611. }
  6612. @objc func contentMessageTapped(_ sender: ObjectGesture) {
  6613. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  6614. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  6615. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  6616. if (sender.image_id != "") {
  6617. if let dirPath = paths.first {
  6618. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.image_id)
  6619. if FileManager.default.fileExists(atPath: imageURL.path) {
  6620. let image = UIImage(contentsOfFile: imageURL.path)
  6621. let previewImageVC = PreviewAttachmentImageVideo(nibName: "PreviewAttachmentImageVideo", bundle: Bundle.resourceBundle(for: Nexilis.self))
  6622. previewImageVC.image = image
  6623. previewImageVC.isHiddenTextField = true
  6624. previewImageVC.modalPresentationStyle = .custom
  6625. previewImageVC.modalTransitionStyle = .crossDissolve
  6626. self.present(previewImageVC, animated: true, completion: nil)
  6627. } else if FileEncryption.shared.isSecureExists(filename: sender.image_id) {
  6628. do {
  6629. let data = try FileEncryption.shared.readSecure(filename: sender.image_id)
  6630. let image = UIImage(data: data!)
  6631. let previewImageVC = PreviewAttachmentImageVideo(nibName: "PreviewAttachmentImageVideo", bundle: Bundle.resourceBundle(for: Nexilis.self))
  6632. previewImageVC.image = image
  6633. previewImageVC.isHiddenTextField = true
  6634. previewImageVC.modalPresentationStyle = .custom
  6635. previewImageVC.modalTransitionStyle = .crossDissolve
  6636. self.present(previewImageVC, animated: true, completion: nil)
  6637. }
  6638. catch {
  6639. print("Error reading secure file")
  6640. }
  6641. } else {
  6642. for view in sender.imageView.subviews {
  6643. if view is UIImageView {
  6644. view.removeFromSuperview()
  6645. }
  6646. }
  6647. let activityIndicator = UIActivityIndicatorView(style: .large)
  6648. activityIndicator.color = .mainColor
  6649. activityIndicator.hidesWhenStopped = true
  6650. activityIndicator.center = CGPoint(x:sender.imageView.frame.width/2,
  6651. y: sender.imageView.frame.height/2)
  6652. activityIndicator.startAnimating()
  6653. sender.imageView.addSubview(activityIndicator)
  6654. Download().startHTTP(forKey: sender.image_id) { (name, progress) in
  6655. guard progress == 100 else {
  6656. return
  6657. }
  6658. do {
  6659. let secureName = try FileEncryption.shared.writeSecure(filename: name)?[0] as? String ?? ""
  6660. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  6661. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  6662. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  6663. if let dirPath = paths.first {
  6664. let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.image_id)
  6665. if FileManager.default.fileExists(atPath: imageURL.path) {
  6666. let image = UIImage(contentsOfFile: imageURL.path)
  6667. let save: Bool = SecureUserDefaults.shared.value(forKey: "saveToGallery") ?? false
  6668. if save {
  6669. UIImageWriteToSavedPhotosAlbum(image!, nil, nil, nil)
  6670. }
  6671. }
  6672. else if FileEncryption.shared.isSecureExists(filename: secureName) {
  6673. if let secureData = try FileEncryption.shared.readSecure(filename: secureName) {
  6674. let image = UIImage(data: secureData)
  6675. let save: Bool = SecureUserDefaults.shared.value(forKey: "saveToGallery") ?? false
  6676. if save {
  6677. UIImageWriteToSavedPhotosAlbum(image!, nil, nil, nil)
  6678. }
  6679. }
  6680. }
  6681. }
  6682. } catch {
  6683. }
  6684. DispatchQueue.main.async {
  6685. activityIndicator.stopAnimating()
  6686. self.tableChatView.reloadRows(at: [sender.indexPath], with: .none)
  6687. }
  6688. }
  6689. }
  6690. }
  6691. } else if (sender.gif_id != "") {
  6692. if let dirPath = paths.first {
  6693. let gifURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.gif_id)
  6694. if FileManager.default.fileExists(atPath: gifURL.path) {
  6695. do {
  6696. let data = try Data(contentsOf: gifURL)
  6697. APIS.openImageNexilis(image: UIImage(), data: data, isGIF: true)
  6698. } catch {
  6699. }
  6700. } else if FileEncryption.shared.isSecureExists(filename: sender.gif_id) {
  6701. do {
  6702. if let secureData = try FileEncryption.shared.readSecure(filename: sender.gif_id) {
  6703. APIS.openImageNexilis(image: UIImage(), data: secureData, isGIF: true)
  6704. }
  6705. } catch {
  6706. }
  6707. }
  6708. }
  6709. } else if (sender.video_id != "") {
  6710. if let dirPath = paths.first {
  6711. let videoURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.video_id)
  6712. if FileManager.default.fileExists(atPath: videoURL.path) {
  6713. let player = AVPlayer(url: videoURL as URL)
  6714. let playerVC = AVPlayerViewController()
  6715. playerVC.modalPresentationStyle = .custom
  6716. playerVC.player = player
  6717. self.present(playerVC, animated: true, completion: nil)
  6718. } else if FileEncryption.shared.isSecureExists(filename: sender.video_id) {
  6719. do {
  6720. if let secureData = try FileEncryption.shared.readSecure(filename: sender.video_id) {
  6721. let cachesDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
  6722. let tempPath = cachesDirectory.appendingPathComponent(sender.video_id)
  6723. try secureData.write(to: tempPath)
  6724. let player = AVPlayer(url: tempPath as URL)
  6725. let playerVC = AVPlayerViewController()
  6726. playerVC.modalPresentationStyle = .custom
  6727. playerVC.player = player
  6728. self.present(playerVC, animated: true, completion: nil)
  6729. }
  6730. } catch {
  6731. }
  6732. } else {
  6733. for view in sender.imageView.subviews {
  6734. if view is UIImageView {
  6735. view.removeFromSuperview()
  6736. }
  6737. }
  6738. let container = UIView()
  6739. sender.imageView.addSubview(container)
  6740. container.translatesAutoresizingMaskIntoConstraints = false
  6741. container.centerXAnchor.constraint(equalTo: sender.imageView.centerXAnchor).isActive = true
  6742. container.centerYAnchor.constraint(equalTo: sender.imageView.centerYAnchor).isActive = true
  6743. container.widthAnchor.constraint(equalToConstant: 50).isActive = true
  6744. container.heightAnchor.constraint(equalToConstant: 50).isActive = true
  6745. let circlePath = UIBezierPath(arcCenter: CGPoint(x: 25, y: 25), radius: 20, startAngle: -(.pi / 2), endAngle: .pi * 2, clockwise: true)
  6746. let trackShape = CAShapeLayer()
  6747. trackShape.path = circlePath.cgPath
  6748. trackShape.fillColor = UIColor.clear.cgColor
  6749. trackShape.lineWidth = 10
  6750. trackShape.strokeColor = UIColor.blueBubbleColor.withAlphaComponent(0.3).cgColor
  6751. container.backgroundColor = .clear
  6752. container.layer.addSublayer(trackShape)
  6753. let shapeLoading = CAShapeLayer()
  6754. shapeLoading.path = circlePath.cgPath
  6755. shapeLoading.fillColor = UIColor.clear.cgColor
  6756. shapeLoading.lineWidth = 10
  6757. shapeLoading.strokeEnd = 0
  6758. shapeLoading.strokeColor = UIColor.blueBubbleColor.cgColor
  6759. container.layer.addSublayer(shapeLoading)
  6760. let imageDownload = UIImageView(image: UIImage(systemName: "arrow.down", withConfiguration: UIImage.SymbolConfiguration(pointSize: 10, weight: .bold, scale: .default)))
  6761. imageDownload.tintColor = .white
  6762. container.addSubview(imageDownload)
  6763. imageDownload.translatesAutoresizingMaskIntoConstraints = false
  6764. imageDownload.centerXAnchor.constraint(equalTo: sender.imageView.centerXAnchor).isActive = true
  6765. imageDownload.centerYAnchor.constraint(equalTo: sender.imageView.centerYAnchor).isActive = true
  6766. imageDownload.widthAnchor.constraint(equalToConstant: 30).isActive = true
  6767. imageDownload.heightAnchor.constraint(equalToConstant: 30).isActive = true
  6768. Download().startHTTP(forKey: sender.video_id, isImage: false) { (name, progress) in
  6769. DispatchQueue.main.async {
  6770. guard progress == 100 else {
  6771. shapeLoading.strokeEnd = CGFloat(progress / 100)
  6772. return
  6773. }
  6774. do {
  6775. if let secureName = try FileEncryption.shared.writeSecure(filename: name)?[0] as? String {
  6776. let secureData = try FileEncryption.shared.readSecure(filename: secureName)
  6777. let cachesDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
  6778. let tempPath = cachesDirectory.appendingPathComponent(name)
  6779. try secureData!.write(to: tempPath)
  6780. let save: Bool = SecureUserDefaults.shared.value(forKey: "saveToGallery") ?? false
  6781. if save {
  6782. PHPhotoLibrary.shared().performChanges({
  6783. PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: tempPath)
  6784. }) { saved, error in
  6785. }
  6786. }
  6787. } else {
  6788. let save: Bool = SecureUserDefaults.shared.value(forKey: "saveToGallery") ?? false
  6789. if save {
  6790. let videoURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.video_id)
  6791. PHPhotoLibrary.shared().performChanges({
  6792. PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: videoURL)
  6793. }) { saved, error in
  6794. }
  6795. }
  6796. }
  6797. } catch {
  6798. }
  6799. let idx = self.dataMessages.firstIndex(where: { $0["video_id"] as? String ?? "" == sender.video_id})
  6800. if idx != nil {
  6801. self.dataMessages[idx!]["progress"] = progress
  6802. self.tableChatView.reloadRows(at: [sender.indexPath], with: .none)
  6803. }
  6804. }
  6805. }
  6806. }
  6807. }
  6808. } else if (sender.file_id != "") {
  6809. if let dirPath = paths.first {
  6810. let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.file_id)
  6811. if FileManager.default.fileExists(atPath: fileURL.path) {
  6812. self.previewItem = fileURL as NSURL
  6813. let previewController = QLPreviewController()
  6814. let rightBarButton = UIBarButtonItem()
  6815. previewController.navigationItem.rightBarButtonItem = rightBarButton
  6816. previewController.dataSource = self
  6817. previewController.modalPresentationStyle = .custom
  6818. self.present(previewController, animated: true)
  6819. } else if FileEncryption.shared.isSecureExists(filename: sender.file_id) {
  6820. do {
  6821. if let docData = try FileEncryption.shared.readSecure(filename: sender.file_id) {
  6822. let cachesDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
  6823. let tempPath = cachesDirectory.appendingPathComponent(sender.file_id)
  6824. try docData.write(to: tempPath)
  6825. self.previewItem = tempPath as NSURL
  6826. let previewController = QLPreviewController()
  6827. let rightBarButton = UIBarButtonItem()
  6828. previewController.navigationItem.rightBarButtonItem = rightBarButton
  6829. previewController.dataSource = self
  6830. previewController.modalPresentationStyle = .custom
  6831. self.present(previewController,animated: true)
  6832. }
  6833. }
  6834. catch {
  6835. }
  6836. } else {
  6837. for view in sender.containerFile.subviews {
  6838. if !(view is UIImageView) && !(view is UILabel) {
  6839. view.removeFromSuperview()
  6840. }
  6841. }
  6842. let containerLoading = UIView()
  6843. sender.containerFile.addSubview(containerLoading)
  6844. containerLoading.translatesAutoresizingMaskIntoConstraints = false
  6845. containerLoading.centerYAnchor.constraint(equalTo: sender.containerFile.centerYAnchor).isActive = true
  6846. containerLoading.leadingAnchor.constraint(equalTo: sender.labelFile.trailingAnchor, constant: 5).isActive = true
  6847. containerLoading.trailingAnchor.constraint(equalTo: sender.containerFile.trailingAnchor, constant: -5).isActive = true
  6848. containerLoading.widthAnchor.constraint(equalToConstant: 30).isActive = true
  6849. containerLoading.heightAnchor.constraint(equalToConstant: 30).isActive = true
  6850. let circlePath = UIBezierPath(arcCenter: CGPoint(x: 15, y: 15), radius: 10, startAngle: -(.pi / 2), endAngle: .pi * 2, clockwise: true)
  6851. let trackShape = CAShapeLayer()
  6852. trackShape.path = circlePath.cgPath
  6853. trackShape.fillColor = UIColor.clear.cgColor
  6854. trackShape.lineWidth = 5
  6855. trackShape.strokeColor = UIColor.blueBubbleColor.withAlphaComponent(0.3).cgColor
  6856. containerLoading.layer.addSublayer(trackShape)
  6857. let shapeLoading = CAShapeLayer()
  6858. shapeLoading.path = circlePath.cgPath
  6859. shapeLoading.fillColor = UIColor.clear.cgColor
  6860. shapeLoading.lineWidth = 3
  6861. shapeLoading.strokeEnd = 0
  6862. shapeLoading.strokeColor = UIColor.blueBubbleColor.cgColor
  6863. containerLoading.layer.addSublayer(shapeLoading)
  6864. let imageupload = UIImageView(image: UIImage(systemName: "arrow.down", withConfiguration: UIImage.SymbolConfiguration(pointSize: 10, weight: .bold, scale: .default)))
  6865. imageupload.tintColor = .white
  6866. containerLoading.addSubview(imageupload)
  6867. imageupload.translatesAutoresizingMaskIntoConstraints = false
  6868. imageupload.centerYAnchor.constraint(equalTo: containerLoading.centerYAnchor).isActive = true
  6869. imageupload.centerXAnchor.constraint(equalTo: containerLoading.centerXAnchor).isActive = true
  6870. Download().startHTTP(forKey: sender.file_id, isImage: false) { (name, progress) in
  6871. DispatchQueue.main.async {
  6872. guard progress == 100 else {
  6873. shapeLoading.strokeEnd = CGFloat(progress / 100)
  6874. return
  6875. }
  6876. do {
  6877. try FileEncryption.shared.writeSecure(filename: name)
  6878. } catch {
  6879. }
  6880. let idx = self.dataMessages.firstIndex(where: { $0["file_id"] as? String ?? "" == sender.file_id})
  6881. if idx != nil {
  6882. self.dataMessages[idx!]["progress"] = progress
  6883. self.tableChatView.reloadRows(at: [sender.indexPath], with: .none)
  6884. }
  6885. }
  6886. }
  6887. }
  6888. }
  6889. } else if !sender.audio_id.isEmpty {
  6890. if let dirPath = paths.first {
  6891. let audioURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.audio_id)
  6892. if FileManager.default.fileExists(atPath: audioURL.path) {
  6893. do {
  6894. if audioPlayer == nil || audioPlayer?.url != audioURL {
  6895. audioPlayer = try AVAudioPlayer(contentsOf: audioURL)
  6896. audioPlayer?.prepareToPlay()
  6897. audioPlayer?.play()
  6898. } else if audioPlayer!.isPlaying {
  6899. audioPlayer?.pause()
  6900. } else {
  6901. audioPlayer?.play()
  6902. }
  6903. } catch {
  6904. }
  6905. } else if FileEncryption.shared.isSecureExists(filename: sender.audio_id) {
  6906. do {
  6907. if let audioData = try FileEncryption.shared.readSecure(filename: sender.audio_id) {
  6908. let cachesDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
  6909. let tempPath = cachesDirectory.appendingPathComponent(sender.audio_id)
  6910. try audioData.write(to: tempPath)
  6911. if audioPlayer == nil || audioPlayer?.url != tempPath {
  6912. audioPlayer = try AVAudioPlayer(contentsOf: tempPath)
  6913. audioPlayer?.prepareToPlay()
  6914. audioPlayer?.play()
  6915. } else if audioPlayer!.isPlaying {
  6916. audioPlayer?.pause()
  6917. } else {
  6918. audioPlayer?.play()
  6919. }
  6920. }
  6921. } catch {
  6922. }
  6923. }
  6924. }
  6925. } else {
  6926. DispatchQueue.main.async {
  6927. let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == sender.message_id})
  6928. if idx == nil {
  6929. return
  6930. }
  6931. let section = self.dataDates.firstIndex(of: self.dataMessages[idx!]["chat_date"] as? String ?? "")
  6932. if section == nil {
  6933. return
  6934. }
  6935. 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})
  6936. if row == nil {
  6937. return
  6938. }
  6939. let indexPath = IndexPath(row: row!, section: section!)
  6940. self.tableChatView.scrollToRow(at: indexPath, at: .middle, animated: true)
  6941. DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
  6942. if let cell = self.tableChatView.cellForRow(at: indexPath) {
  6943. let containerMessage = cell.contentView.subviews[0]
  6944. let idMe = User.getMyPin() as String?
  6945. if (self.dataMessages[idx!]["f_pin"] as? String == idMe) {
  6946. containerMessage.backgroundColor = .blueBubbleColor.withAlphaComponent(0.3)
  6947. DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
  6948. if (self.dataMessages[idx!]["attachment_flag"] as? String == "11") {
  6949. containerMessage.backgroundColor = .clear
  6950. } else {
  6951. containerMessage.backgroundColor = .blueBubbleColor
  6952. }
  6953. }
  6954. } else {
  6955. containerMessage.backgroundColor = .whiteBubbleColor.withAlphaComponent(0.3)
  6956. DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
  6957. if (self.dataMessages[idx!]["attachment_flag"] as? String == "11") {
  6958. containerMessage.backgroundColor = .clear
  6959. } else {
  6960. containerMessage.backgroundColor = .whiteBubbleColor
  6961. }
  6962. }
  6963. }
  6964. }
  6965. }
  6966. }
  6967. }
  6968. }
  6969. func highlightedText(for text: String, textView: UITextView) -> NSAttributedString {
  6970. let mutableAttributedString = textView.attributedText!.mutableCopy() as! NSMutableAttributedString
  6971. if let range = textView.attributedText.string.range(of: text) {
  6972. let nsRange = NSRange(range, in: textView.attributedText.string)
  6973. mutableAttributedString.addAttribute(.backgroundColor, value: UIColor.lightGray.withAlphaComponent(0.5), range: NSRange(range, in: text))
  6974. }
  6975. return mutableAttributedString
  6976. }
  6977. func removeHighlightedText(for text: String, textView: UITextView) -> NSAttributedString {
  6978. let mutableAttributedString = textView.attributedText!.mutableCopy() as! NSMutableAttributedString
  6979. if let range = textView.attributedText.string.range(of: text) {
  6980. let nsRange = NSRange(range, in: textView.attributedText.string)
  6981. mutableAttributedString.removeAttribute(.backgroundColor, range: NSRange(range, in: text))
  6982. }
  6983. return mutableAttributedString
  6984. }
  6985. @objc func tapMessageText(_ sender: ObjectGesture) {
  6986. var stringURl = sender.message_id
  6987. if stringURl.lowercased().starts(with: "www.") {
  6988. stringURl = "https://" + stringURl.replacingOccurrences(of: "www.", with: "")
  6989. }
  6990. guard let url = URL(string: stringURl) else { return }
  6991. UIApplication.shared.open(url)
  6992. }
  6993. // public func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
  6994. // if copySession || forwardSession || deleteSession {
  6995. // return nil
  6996. // }
  6997. // let idMe = User.getMyPin() as String?
  6998. // if (dataMessages[indexPath.row]["f_pin"] as? String != idMe) {
  6999. // return nil
  7000. // }
  7001. // let messageInfoVC = MessageInfo()
  7002. // messageInfoVC.data = dataMessages[indexPath.row]
  7003. // self.navigationController?.show(messageInfoVC, sender: nil)
  7004. // return UISwipeActionsConfiguration()
  7005. // }
  7006. //
  7007. // public func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
  7008. // if copySession || forwardSession || deleteSession {
  7009. // return nil
  7010. // }
  7011. // let action = UIContextualAction(style: .normal,
  7012. // title: "") { [weak self] (action, view, completionHandler) in
  7013. // self?.handleReply(indexPath: indexPath)
  7014. // completionHandler(true)
  7015. // }
  7016. // action.backgroundColor = .white
  7017. // action.image = UIImage(systemName: "arrowshape.turn.up.left.fill")?.withTintColor(.black, renderingMode: .alwaysOriginal)
  7018. // return UISwipeActionsConfiguration(actions: [action])
  7019. // }
  7020. private func handleReply(indexPath: IndexPath, dataMessagesImage: [String: Any?] = [:], reffId: String = "") {
  7021. var dataMessages = self.dataMessages.filter({ $0["chat_date"] as? String ?? "" == dataDates[indexPath.section]})
  7022. if reffId.isEmpty {
  7023. self.deleteReplyView()
  7024. if dataMessagesImage.count != 0 {
  7025. dataMessages = [dataMessagesImage]
  7026. } else {
  7027. self.textFieldSend.becomeFirstResponder()
  7028. }
  7029. self.reffId = dataMessages[indexPath.row]["message_id"] as? String
  7030. } else {
  7031. dataMessages = self.dataMessages.filter({ $0["message_id"] as? String ?? "" == reffId })
  7032. self.reffId = reffId
  7033. }
  7034. UIView.animate(withDuration: 0.25, delay: 0.0, options: .curveEaseInOut, animations: {
  7035. self.constraintTopTextField.constant = self.constraintTopTextField.constant + 50
  7036. }, completion: nil)
  7037. if (self.currentIndexpath != nil) {
  7038. DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
  7039. self.tableChatView.scrollToRow(at: IndexPath(row: self.currentIndexpath!.row, section: self.currentIndexpath!.section), at: .none, animated: false)
  7040. }
  7041. } else {
  7042. self.tableChatView.scrollToBottom()
  7043. }
  7044. self.viewTextfield.addSubview(self.containerPreviewReply)
  7045. self.containerPreviewReply.translatesAutoresizingMaskIntoConstraints = false
  7046. self.containerPreviewReply.leadingAnchor.constraint(equalTo: self.viewTextfield.leadingAnchor).isActive = true
  7047. self.containerPreviewReply.topAnchor.constraint(equalTo: self.viewTextfield.topAnchor).isActive = true
  7048. if !self.containerLink.isDescendant(of: self.viewTextfield) {
  7049. self.bottomAnchorPreviewReply = self.containerPreviewReply.bottomAnchor.constraint(equalTo: self.textFieldSend.topAnchor)
  7050. } else {
  7051. self.bottomAnchorPreviewReply = self.containerPreviewReply.bottomAnchor.constraint(equalTo: self.containerLink.topAnchor)
  7052. }
  7053. self.bottomAnchorPreviewReply.isActive = true
  7054. self.containerPreviewReply.trailingAnchor.constraint(equalTo: self.viewTextfield.trailingAnchor).isActive = true
  7055. self.containerPreviewReply.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : .secondaryColor
  7056. let leftReply = UIView()
  7057. self.containerPreviewReply.addSubview(leftReply)
  7058. leftReply.translatesAutoresizingMaskIntoConstraints = false
  7059. leftReply.leadingAnchor.constraint(equalTo: self.viewTextfield.leadingAnchor).isActive = true
  7060. leftReply.topAnchor.constraint(equalTo: self.containerPreviewReply.topAnchor).isActive = true
  7061. leftReply.bottomAnchor.constraint(equalTo: self.containerPreviewReply.bottomAnchor).isActive = true
  7062. leftReply.widthAnchor.constraint(equalToConstant: 3).isActive = true
  7063. leftReply.backgroundColor = .orangeColor
  7064. let titleReply = UILabel()
  7065. self.containerPreviewReply.addSubview(titleReply)
  7066. titleReply.translatesAutoresizingMaskIntoConstraints = false
  7067. titleReply.leadingAnchor.constraint(equalTo: leftReply.leadingAnchor, constant: 10).isActive = true
  7068. titleReply.topAnchor.constraint(equalTo: self.containerPreviewReply.topAnchor, constant: 10).isActive = true
  7069. titleReply.font = UIFont.systemFont(ofSize: 12).bold
  7070. let idMe = User.getMyPin() as String?
  7071. if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
  7072. titleReply.text = "You".localized()
  7073. } else {
  7074. if self.isContactCenter {
  7075. let user: [User] = self.users.filter({$0.pin == dataMessages[indexPath.row]["f_pin"] as? String})
  7076. titleReply.text = user.first!.fullName
  7077. } else {
  7078. titleReply.text = self.dataPerson["name"]!!
  7079. }
  7080. }
  7081. titleReply.textColor = .orangeColor
  7082. let contentReply = UILabel()
  7083. self.containerPreviewReply.addSubview(contentReply)
  7084. contentReply.translatesAutoresizingMaskIntoConstraints = false
  7085. contentReply.leadingAnchor.constraint(equalTo: leftReply.leadingAnchor, constant: 10).isActive = true
  7086. contentReply.topAnchor.constraint(equalTo: titleReply.bottomAnchor).isActive = true
  7087. contentReply.font = UIFont.systemFont(ofSize: 10)
  7088. let message_text = dataMessages[indexPath.row]["message_text"] as? String ?? ""
  7089. let attachment_flag = dataMessages[indexPath.row]["attachment_flag"] as? String ?? ""
  7090. let thumb_chat = dataMessages[indexPath.row]["thumb_id"] as? String ?? ""
  7091. let image_chat = dataMessages[indexPath.row]["image_id"] as? String ?? ""
  7092. let video_chat = dataMessages[indexPath.row]["video_id"] as? String ?? ""
  7093. let file_chat = dataMessages[indexPath.row]["file_id"] as? String ?? ""
  7094. if (attachment_flag == "0" && thumb_chat == "") {
  7095. contentReply.attributedText = message_text.richText()
  7096. } else if (attachment_flag == "1" || image_chat != "") {
  7097. if (message_text == "") {
  7098. contentReply.text = "📷 Photo".localized()
  7099. } else {
  7100. contentReply.attributedText = message_text.richText()
  7101. }
  7102. } else if (attachment_flag == "2" || video_chat != "") {
  7103. if (message_text == "") {
  7104. contentReply.text = "📹 Video".localized()
  7105. } else {
  7106. contentReply.attributedText = message_text.richText()
  7107. }
  7108. } else if (attachment_flag == "6" || file_chat != ""){
  7109. contentReply.text = "📄 \(message_text.components(separatedBy: "|")[0])"
  7110. } else if (attachment_flag == "11") {
  7111. contentReply.text = "❤️ Sticker"
  7112. }
  7113. contentReply.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .gray
  7114. let buttonCancelReply = UIButton(type: .custom)
  7115. self.containerPreviewReply.addSubview(buttonCancelReply)
  7116. buttonCancelReply.translatesAutoresizingMaskIntoConstraints = false
  7117. buttonCancelReply.trailingAnchor.constraint(equalTo: self.containerPreviewReply.trailingAnchor, constant: -10).isActive = true
  7118. buttonCancelReply.centerYAnchor.constraint(equalTo: self.containerPreviewReply.centerYAnchor).isActive = true
  7119. buttonCancelReply.setImage(UIImage(systemName: "xmark.circle" , withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .regular, scale: .default)), for: .normal)
  7120. buttonCancelReply.addTarget(nil, action: #selector(self.deleteReplyView), for: .touchUpInside)
  7121. buttonCancelReply.backgroundColor = .clear
  7122. buttonCancelReply.tintColor = .mainColor
  7123. if (attachment_flag == "1" || attachment_flag == "2" || image_chat != "" || video_chat != "") {
  7124. let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
  7125. let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
  7126. let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
  7127. if let dirPath = paths.first {
  7128. let thumbURL = URL(fileURLWithPath: dirPath).appendingPathComponent(thumb_chat)
  7129. let image : UIImage? = {
  7130. if let img = Nexilis.imageCache.object(forKey: thumb_chat as NSString) {
  7131. return img
  7132. }
  7133. else if let img = UIImage(contentsOfFile: thumbURL.path)?.resize(target: CGSize(width: 500, height: 500)) {
  7134. Nexilis.imageCache.setObject(img, forKey: thumb_chat as NSString)
  7135. return img
  7136. }
  7137. return nil
  7138. }()
  7139. // let image = UIGraphicsRenderer.renderImageAt(url: thumbURL as NSURL, size: CGSize(width: 250, height: 250))
  7140. let imageThumb = UIImageView(image: image)
  7141. self.containerPreviewReply.addSubview(imageThumb)
  7142. imageThumb.layer.cornerRadius = 2.0
  7143. imageThumb.clipsToBounds = true
  7144. imageThumb.translatesAutoresizingMaskIntoConstraints = false
  7145. imageThumb.trailingAnchor.constraint(equalTo: buttonCancelReply.leadingAnchor, constant: -10).isActive = true
  7146. imageThumb.centerYAnchor.constraint(equalTo: self.containerPreviewReply.centerYAnchor).isActive = true
  7147. imageThumb.widthAnchor.constraint(equalToConstant: 30).isActive = true
  7148. imageThumb.heightAnchor.constraint(equalToConstant: 30).isActive = true
  7149. if (attachment_flag == "2") {
  7150. let imagePlay = UIImageView(image: UIImage(systemName: "play.circle.fill"))
  7151. imageThumb.addSubview(imagePlay)
  7152. imagePlay.clipsToBounds = true
  7153. imagePlay.translatesAutoresizingMaskIntoConstraints = false
  7154. imagePlay.centerYAnchor.constraint(equalTo: imageThumb.centerYAnchor).isActive = true
  7155. imagePlay.centerXAnchor.constraint(equalTo: imageThumb.centerXAnchor).isActive = true
  7156. imagePlay.widthAnchor.constraint(equalToConstant: 10).isActive = true
  7157. imagePlay.heightAnchor.constraint(equalToConstant: 10).isActive = true
  7158. imagePlay.tintColor = .white
  7159. }
  7160. }
  7161. }
  7162. if (attachment_flag == "11") {
  7163. let imageSticker = UIImageView(image: UIImage(named: (message_text.components(separatedBy: "/")[1]), in: Bundle.resourceBundle(for: Nexilis.self), with: nil))
  7164. self.containerPreviewReply.addSubview(imageSticker)
  7165. imageSticker.layer.cornerRadius = 2.0
  7166. imageSticker.clipsToBounds = true
  7167. imageSticker.translatesAutoresizingMaskIntoConstraints = false
  7168. imageSticker.trailingAnchor.constraint(equalTo: buttonCancelReply.leadingAnchor, constant: -10).isActive = true
  7169. imageSticker.centerYAnchor.constraint(equalTo: self.containerPreviewReply.centerYAnchor).isActive = true
  7170. imageSticker.widthAnchor.constraint(equalToConstant: 30).isActive = true
  7171. imageSticker.heightAnchor.constraint(equalToConstant: 30).isActive = true
  7172. }
  7173. }
  7174. func scrollToFirstSearchMessage(indexScroll: Int = 1) {
  7175. if textSearch.count < 2 {
  7176. return
  7177. }
  7178. var lastIndex = 0
  7179. let messageTextForSearch: [[String: Any?]] = self.dataMessages.reversed()
  7180. for idx in 0..<messageTextForSearch.count {
  7181. if (messageTextForSearch[idx]["message_text"] as? String ?? "").lowercased().contains(textSearch) {
  7182. lastIndex += 1
  7183. if lastIndex < indexScroll {
  7184. continue
  7185. }
  7186. lastScrollIdxSearch = lastIndex
  7187. let section = self.dataDates.firstIndex(of: messageTextForSearch[idx]["chat_date"] as? String ?? "")
  7188. if section == nil {
  7189. return
  7190. }
  7191. 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})
  7192. if row == nil {
  7193. return
  7194. }
  7195. let indexPath = IndexPath(row: row!, section: section!)
  7196. self.tableChatView.scrollToRow(at: indexPath, at: .middle, animated: true)
  7197. DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
  7198. if let cell = self.tableChatView.cellForRow(at: indexPath) {
  7199. let containerMessage = cell.contentView.subviews[0]
  7200. let idMe = User.getMyPin() as String?
  7201. if (messageTextForSearch[idx]["f_pin"] as? String == idMe) {
  7202. containerMessage.backgroundColor = .blueBubbleColor.withAlphaComponent(0.3)
  7203. DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
  7204. if (messageTextForSearch[idx]["attachment_flag"] as? String == "11") {
  7205. containerMessage.backgroundColor = .clear
  7206. } else {
  7207. containerMessage.backgroundColor = .blueBubbleColor
  7208. }
  7209. }
  7210. } else {
  7211. containerMessage.backgroundColor = .whiteBubbleColor.withAlphaComponent(0.3)
  7212. DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
  7213. if (messageTextForSearch[idx]["attachment_flag"] as? String == "11") {
  7214. containerMessage.backgroundColor = .clear
  7215. } else {
  7216. containerMessage.backgroundColor = .whiteBubbleColor
  7217. }
  7218. }
  7219. }
  7220. }
  7221. }
  7222. titleSearchMatches.isHidden = false
  7223. if countMatchesSearch != 0 {
  7224. if countMatchesSearch > 1 {
  7225. titleSearchMatches.text = "\(lastScrollIdxSearch) " + "of".localized() + " \(countMatchesSearch) " + "matches".localized()
  7226. } else {
  7227. titleSearchMatches.text = "\(countMatchesSearch) " + "matches".localized()
  7228. }
  7229. } else {
  7230. titleSearchMatches.text = "Not found".localized()
  7231. }
  7232. if lastScrollIdxSearch == countMatchesSearch || countMatchesSearch == 0 {
  7233. buttonUp.isEnabled = false
  7234. buttonUp.tintColor = .gray
  7235. } else {
  7236. buttonUp.isEnabled = true
  7237. buttonUp.tintColor = .mainColor
  7238. }
  7239. if countMatchesSearch == 0 || lastScrollIdxSearch == 1 || countMatchesSearch == 1 {
  7240. buttonDown.isEnabled = false
  7241. buttonDown.tintColor = .gray
  7242. } else {
  7243. buttonDown.isEnabled = true
  7244. buttonDown.tintColor = .mainColor
  7245. }
  7246. break
  7247. }
  7248. }
  7249. }
  7250. public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
  7251. let indexPath = tableChatView.indexPathsForVisibleRows?.first
  7252. if indexPath != nil {
  7253. let headerRect = tableChatView.rectForHeader(inSection: indexPath!.section)
  7254. let isPinned = headerRect.origin.y <= scrollView.contentOffset.y
  7255. if listViewOnSection.count != 0 && listViewOnSection.count - 1 == indexPath!.section && indexPath!.row > 0 {
  7256. let sect = listViewOnSection.count - 1 < currentIndexpath!.section ? listViewOnSection.count - 1 : currentIndexpath!.section
  7257. let headerView = listViewOnSection[sect]
  7258. headerView.isHidden = true
  7259. }
  7260. }
  7261. }
  7262. public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
  7263. if !decelerate {
  7264. let indexPath = tableChatView.indexPathsForVisibleRows?.first
  7265. if indexPath != nil {
  7266. let headerRect = tableChatView.rectForHeader(inSection: indexPath!.section)
  7267. let isPinned = headerRect.origin.y <= scrollView.contentOffset.y
  7268. if listViewOnSection.count != 0 && listViewOnSection.count - 1 == indexPath!.section && isPinned {
  7269. let sect = listViewOnSection.count - 1 < currentIndexpath!.section ? listViewOnSection.count - 1 : currentIndexpath!.section
  7270. let headerView = listViewOnSection[sect]
  7271. headerView.isHidden = true
  7272. }
  7273. }
  7274. }
  7275. }
  7276. }
  7277. extension UITableView {
  7278. func scrollToBottom(isAnimated:Bool = true){
  7279. DispatchQueue.main.async {
  7280. if self.numberOfSections == 0 {
  7281. return
  7282. }
  7283. let indexPath = IndexPath(
  7284. row: self.numberOfRows(inSection: self.numberOfSections-1) - 1,
  7285. section: self.numberOfSections - 1)
  7286. if indexPath.row != -1 {
  7287. self.scrollToRow(at: indexPath, at: .bottom, animated: isAnimated)
  7288. }
  7289. }
  7290. }
  7291. func scrollToTop(isAnimated:Bool = true) {
  7292. DispatchQueue.main.async {
  7293. let indexPath = IndexPath(row: 0, section: 0)
  7294. if indexPath.row != -1 {
  7295. self.scrollToRow(at: indexPath, at: .top, animated: isAnimated)
  7296. }
  7297. }
  7298. }
  7299. }
  7300. extension UIImage {
  7301. public func imageWithInsets(insets: UIEdgeInsets) -> UIImage? {
  7302. UIGraphicsBeginImageContextWithOptions(
  7303. CGSize(width: self.size.width + insets.left + insets.right,
  7304. height: self.size.height + insets.top + insets.bottom), false, self.scale)
  7305. let _ = UIGraphicsGetCurrentContext()
  7306. let origin = CGPoint(x: insets.left, y: insets.top)
  7307. self.draw(at: origin)
  7308. let imageWithInsets = UIGraphicsGetImageFromCurrentImageContext()
  7309. UIGraphicsEndImageContext()
  7310. return imageWithInsets
  7311. }
  7312. }
  7313. extension EditorPersonal: UISearchBarDelegate {
  7314. public func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
  7315. textSearch = searchText.trimmingCharacters(in: .whitespacesAndNewlines)
  7316. countMatchesSearch = 0
  7317. titleSearchMatches.isHidden = true
  7318. tableChatView.reloadData()
  7319. scrollToFirstSearchMessage()
  7320. }
  7321. }
  7322. public class ObjectGesture: UITapGestureRecognizer {
  7323. public var message_id = ""
  7324. public var image_id = ""
  7325. public var video_id = ""
  7326. public var file_id = ""
  7327. public var audio_id = ""
  7328. public var gif_id = ""
  7329. public var imageView = UIImageView()
  7330. public var containerFile = UIView()
  7331. public var labelFile = UILabel()
  7332. public var videoURL: NSURL?
  7333. public var indexPath = IndexPath()
  7334. public var indexImageTapped: Int!
  7335. public var listImageFromGrouping: [ImageGrouping]!
  7336. public var isInitiator: Bool!
  7337. }
  7338. class navigationQLPreviewDocument: UIBarButtonItem {
  7339. var navigation = UINavigationController()
  7340. }
  7341. class segmentedControllerObject: UISegmentedControl {
  7342. var navigation = UINavigationController()
  7343. }
  7344. public class ImageGrouping {
  7345. public var messageId = ""
  7346. public var thumbId = ""
  7347. public var imageId = ""
  7348. public var status = ""
  7349. public var time = ""
  7350. public var lPin = ""
  7351. public var dataMessage: [String: Any?] = [:]
  7352. public var dataPerson: [String: String?] = [:]
  7353. public var dataGroup: [String: Any?] = [:]
  7354. public var dataTopic: [String: Any?] = [:]
  7355. public var isSelected = false
  7356. 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?]) {
  7357. self.messageId = messageId
  7358. self.thumbId = thumbId
  7359. self.imageId = imageId
  7360. self.status = status
  7361. self.time = time
  7362. self.lPin = lPin
  7363. self.dataMessage = dataMessage
  7364. self.dataPerson = dataPerson
  7365. self.dataGroup = dataGroup
  7366. self.dataTopic = dataTopic
  7367. }
  7368. }
  7369. public class TypeDataMessage {
  7370. public static let message_id = "message_id"
  7371. public static let f_pin = "f_pin"
  7372. public static let l_pin = "l_pin"
  7373. public static let message_scope_id = "message_scope_id"
  7374. public static let server_date = "server_date"
  7375. public static let status = "status"
  7376. public static let message_text = "message_text"
  7377. public static let audio_id = "audio_id"
  7378. public static let video_id = "video_id"
  7379. public static let image_id = "image_id"
  7380. public static let thumb_id = "thumb_id"
  7381. public static let read_receipts = "read_receipts"
  7382. public static let chat_id = "chat_id"
  7383. public static let file_id = "file_id"
  7384. public static let attachment_flag = "attachment_flag"
  7385. public static let reff_id = "reff_id"
  7386. public static let lock = "lock"
  7387. public static let is_stared = "is_stared"
  7388. public static let blog_id = "blog_id"
  7389. public static let credential = "credential"
  7390. public static let progress = "progress"
  7391. public static let chat_date = "chat_date"
  7392. public static let is_call_center = "is_call_center"
  7393. public static let call_center_id = "call_center_id"
  7394. public static let opposite_pin = "opposite_pin"
  7395. public static let last_edit = "last_edit"
  7396. public static let gif_id = "gif_id"
  7397. public static let is_forwarded = "is_forwarded"
  7398. public static let is_secret = "is_secret"
  7399. }