EditorPersonal.swift 410 KB

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