history_widget.cpp 249 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "history/history_widget.h"
  8. #include "api/api_editing.h"
  9. #include "api/api_bot.h"
  10. #include "api/api_chat_participants.h"
  11. #include "api/api_report.h"
  12. #include "api/api_sending.h"
  13. #include "api/api_send_progress.h"
  14. #include "api/api_unread_things.h"
  15. #include "ui/boxes/confirm_box.h"
  16. #include "boxes/delete_messages_box.h"
  17. #include "boxes/send_credits_box.h"
  18. #include "boxes/send_files_box.h"
  19. #include "boxes/share_box.h"
  20. #include "boxes/edit_caption_box.h"
  21. #include "boxes/moderate_messages_box.h"
  22. #include "boxes/premium_limits_box.h"
  23. #include "boxes/premium_preview_box.h"
  24. #include "boxes/star_gift_box.h"
  25. #include "boxes/peers/edit_peer_permissions_box.h" // ShowAboutGigagroup.
  26. #include "boxes/peers/edit_peer_requests_box.h"
  27. #include "core/file_utilities.h"
  28. #include "core/mime_type.h"
  29. #include "ui/emoji_config.h"
  30. #include "ui/chat/attach/attach_prepare.h"
  31. #include "ui/chat/choose_theme_controller.h"
  32. #include "ui/widgets/menu/menu_add_action_callback_factory.h"
  33. #include "ui/widgets/buttons.h"
  34. #include "ui/widgets/inner_dropdown.h"
  35. #include "ui/widgets/dropdown_menu.h"
  36. #include "ui/widgets/labels.h"
  37. #include "ui/effects/ripple_animation.h"
  38. #include "ui/effects/message_sending_animation_controller.h"
  39. #include "ui/text/text_utilities.h" // Ui::Text::ToUpper
  40. #include "ui/text/format_values.h"
  41. #include "ui/chat/message_bar.h"
  42. #include "ui/chat/attach/attach_send_files_way.h"
  43. #include "ui/chat/choose_send_as.h"
  44. #include "ui/effects/spoiler_mess.h"
  45. #include "ui/image/image.h"
  46. #include "ui/painter.h"
  47. #include "ui/rect.h"
  48. #include "ui/power_saving.h"
  49. #include "ui/controls/emoji_button.h"
  50. #include "ui/controls/send_button.h"
  51. #include "ui/controls/send_as_button.h"
  52. #include "ui/controls/silent_toggle.h"
  53. #include "ui/ui_utility.h"
  54. #include "inline_bots/inline_bot_result.h"
  55. #include "base/event_filter.h"
  56. #include "base/qt_signal_producer.h"
  57. #include "base/qt/qt_key_modifiers.h"
  58. #include "base/unixtime.h"
  59. #include "base/call_delayed.h"
  60. #include "data/business/data_shortcut_messages.h"
  61. #include "data/components/credits.h"
  62. #include "data/components/scheduled_messages.h"
  63. #include "data/components/sponsored_messages.h"
  64. #include "data/notify/data_notify_settings.h"
  65. #include "data/data_changes.h"
  66. #include "data/data_drafts.h"
  67. #include "data/data_session.h"
  68. #include "data/data_web_page.h"
  69. #include "data/data_document.h"
  70. #include "data/data_photo.h"
  71. #include "data/data_photo_media.h"
  72. #include "data/data_channel.h"
  73. #include "data/data_chat.h"
  74. #include "data/data_forum.h"
  75. #include "data/data_forum_topic.h"
  76. #include "data/data_user.h"
  77. #include "data/data_chat_filters.h"
  78. #include "data/data_file_origin.h"
  79. #include "data/data_histories.h"
  80. #include "data/data_group_call.h"
  81. #include "data/data_message_reactions.h"
  82. #include "data/data_peer_values.h" // Data::AmPremiumValue.
  83. #include "data/data_premium_limits.h" // Data::PremiumLimits.
  84. #include "data/stickers/data_stickers.h"
  85. #include "data/stickers/data_custom_emoji.h"
  86. #include "history/history.h"
  87. #include "history/history_item.h"
  88. #include "history/history_item_helpers.h" // GetErrorForSending.
  89. #include "history/history_drag_area.h"
  90. #include "history/history_inner_widget.h"
  91. #include "history/history_item_components.h"
  92. #include "history/history_unread_things.h"
  93. #include "history/view/controls/history_view_characters_limit.h"
  94. #include "history/view/controls/history_view_compose_search.h"
  95. #include "history/view/controls/history_view_forward_panel.h"
  96. #include "history/view/controls/history_view_draft_options.h"
  97. #include "history/view/controls/history_view_voice_record_bar.h"
  98. #include "history/view/controls/history_view_ttl_button.h"
  99. #include "history/view/controls/history_view_webpage_processor.h"
  100. #include "history/view/reactions/history_view_reactions_button.h"
  101. #include "history/view/history_view_cursor_state.h"
  102. #include "history/view/history_view_service_message.h"
  103. #include "history/view/history_view_element.h"
  104. #include "history/view/history_view_scheduled_section.h"
  105. #include "history/view/history_view_schedule_box.h"
  106. #include "history/view/history_view_top_bar_widget.h"
  107. #include "history/view/history_view_contact_status.h"
  108. #include "history/view/history_view_paid_reaction_toast.h"
  109. #include "history/view/history_view_pinned_tracker.h"
  110. #include "history/view/history_view_pinned_section.h"
  111. #include "history/view/history_view_pinned_bar.h"
  112. #include "history/view/history_view_group_call_bar.h"
  113. #include "history/view/history_view_item_preview.h"
  114. #include "history/view/history_view_reply.h"
  115. #include "history/view/history_view_requests_bar.h"
  116. #include "history/view/history_view_sticker_toast.h"
  117. #include "history/view/history_view_translate_bar.h"
  118. #include "history/view/media/history_view_media.h"
  119. #include "profile/profile_block_group_members.h"
  120. #include "core/click_handler_types.h"
  121. #include "chat_helpers/field_autocomplete.h"
  122. #include "chat_helpers/tabbed_panel.h"
  123. #include "chat_helpers/tabbed_selector.h"
  124. #include "chat_helpers/tabbed_section.h"
  125. #include "chat_helpers/bot_keyboard.h"
  126. #include "chat_helpers/message_field.h"
  127. #include "menu/menu_send.h"
  128. #include "mtproto/mtproto_config.h"
  129. #include "lang/lang_keys.h"
  130. #include "settings/business/settings_quick_replies.h"
  131. #include "settings/settings_credits_graphics.h"
  132. #include "storage/localimageloader.h"
  133. #include "storage/storage_account.h"
  134. #include "storage/file_upload.h"
  135. #include "storage/storage_media_prepare.h"
  136. #include "media/audio/media_audio.h"
  137. #include "media/audio/media_audio_capture.h"
  138. #include "media/player/media_player_instance.h"
  139. #include "core/application.h"
  140. #include "apiwrap.h"
  141. #include "base/qthelp_regex.h"
  142. #include "ui/boxes/report_box_graphics.h"
  143. #include "ui/chat/pinned_bar.h"
  144. #include "ui/chat/group_call_bar.h"
  145. #include "ui/chat/requests_bar.h"
  146. #include "ui/chat/chat_theme.h"
  147. #include "ui/chat/chat_style.h"
  148. #include "ui/chat/continuous_scroll.h"
  149. #include "ui/widgets/checkbox.h"
  150. #include "ui/widgets/elastic_scroll.h"
  151. #include "ui/widgets/popup_menu.h"
  152. #include "ui/item_text_options.h"
  153. #include "main/main_session.h"
  154. #include "main/main_session_settings.h"
  155. #include "main/session/send_as_peers.h"
  156. #include "webrtc/webrtc_environment.h"
  157. #include "window/notifications_manager.h"
  158. #include "window/window_adaptive.h"
  159. #include "window/window_controller.h"
  160. #include "window/window_session_controller.h"
  161. #include "window/window_slide_animation.h"
  162. #include "window/window_peer_menu.h"
  163. #include "inline_bots/inline_results_widget.h"
  164. #include "inline_bots/bot_attach_web_view.h"
  165. #include "info/profile/info_profile_values.h" // SharedMediaCountValue.
  166. #include "chat_helpers/emoji_suggestions_widget.h"
  167. #include "core/shortcuts.h"
  168. #include "core/ui_integration.h"
  169. #include "support/support_common.h"
  170. #include "support/support_autocomplete.h"
  171. #include "support/support_helper.h"
  172. #include "support/support_preload.h"
  173. #include "dialogs/dialogs_key.h"
  174. #include "calls/calls_instance.h"
  175. #include "styles/style_chat.h"
  176. #include "styles/style_window.h"
  177. #include "styles/style_chat_helpers.h"
  178. #include "styles/style_info.h"
  179. #include <QtGui/QWindow>
  180. #include <QtCore/QMimeData>
  181. namespace {
  182. constexpr auto kMessagesPerPageFirst = 30;
  183. constexpr auto kMessagesPerPage = 50;
  184. constexpr auto kPreloadHeightsCount = 3; // when 3 screens to scroll left make a preload request
  185. constexpr auto kScrollToVoiceAfterScrolledMs = 1000;
  186. constexpr auto kSkipRepaintWhileScrollMs = 100;
  187. constexpr auto kShowMembersDropdownTimeoutMs = 300;
  188. constexpr auto kDisplayEditTimeWarningMs = 300 * 1000;
  189. constexpr auto kFullDayInMs = 86400 * 1000;
  190. constexpr auto kSaveDraftTimeout = crl::time(1000);
  191. constexpr auto kSaveDraftAnywayTimeout = 5 * crl::time(1000);
  192. constexpr auto kSaveCloudDraftIdleTimeout = 14 * crl::time(1000);
  193. constexpr auto kRefreshSlowmodeLabelTimeout = crl::time(200);
  194. constexpr auto kCommonModifiers = 0
  195. | Qt::ShiftModifier
  196. | Qt::MetaModifier
  197. | Qt::ControlModifier;
  198. const auto kPsaAboutPrefix = "cloud_lng_about_psa_";
  199. [[nodiscard]] rpl::producer<PeerData*> ActivePeerValue(
  200. not_null<Window::SessionController*> controller) {
  201. return controller->activeChatValue(
  202. ) | rpl::map([](Dialogs::Key key) {
  203. const auto history = key.history();
  204. return history ? history->peer.get() : nullptr;
  205. });
  206. }
  207. [[nodiscard]] QString FirstEmoji(const QString &s) {
  208. const auto begin = s.data();
  209. const auto end = begin + s.size();
  210. for (auto ch = begin; ch != end; ch++) {
  211. auto length = 0;
  212. if (const auto e = Ui::Emoji::Find(ch, end, &length)) {
  213. return e->text();
  214. }
  215. }
  216. return QString();
  217. }
  218. } // namespace
  219. HistoryWidget::HistoryWidget(
  220. QWidget *parent,
  221. not_null<Window::SessionController*> controller)
  222. : Window::AbstractSectionWidget(
  223. parent,
  224. controller,
  225. ActivePeerValue(controller))
  226. , _api(&controller->session().mtp())
  227. , _updateEditTimeLeftDisplay([=] { updateField(); })
  228. , _fieldBarCancel(this, st::historyReplyCancel)
  229. , _topBar(this, controller)
  230. , _scroll(
  231. this,
  232. controller->chatStyle()->value(lifetime(), st::historyScroll),
  233. false)
  234. , _updateHistoryItems([=] { updateHistoryItemsByTimer(); })
  235. , _cornerButtons(
  236. _scroll.data(),
  237. controller->chatStyle(),
  238. static_cast<HistoryView::CornerButtonsDelegate*>(this))
  239. , _supportAutocomplete(session().supportMode()
  240. ? object_ptr<Support::Autocomplete>(this, &session())
  241. : nullptr)
  242. , _send(std::make_shared<Ui::SendButton>(this, st::historySend))
  243. , _unblock(
  244. this,
  245. tr::lng_unblock_button(tr::now).toUpper(),
  246. st::historyUnblock)
  247. , _botStart(
  248. this,
  249. tr::lng_bot_start(tr::now).toUpper(),
  250. st::historyComposeButton)
  251. , _joinChannel(
  252. this,
  253. tr::lng_profile_join_channel(tr::now).toUpper(),
  254. st::historyComposeButton)
  255. , _muteUnmute(
  256. this,
  257. tr::lng_channel_mute(tr::now).toUpper(),
  258. st::historyComposeButton)
  259. , _reportMessages(this, QString(), st::historyComposeButton)
  260. , _attachToggle(this, st::historyAttach)
  261. , _tabbedSelectorToggle(this, st::historyAttachEmoji)
  262. , _botKeyboardShow(this, st::historyBotKeyboardShow)
  263. , _botKeyboardHide(this, st::historyBotKeyboardHide)
  264. , _botCommandStart(this, st::historyBotCommandStart)
  265. , _voiceRecordBar(std::make_unique<VoiceRecordBar>(
  266. this,
  267. controller->uiShow(),
  268. _send,
  269. st::historySendSize.height()))
  270. , _forwardPanel(std::make_unique<ForwardPanel>([=] { updateField(); }))
  271. , _field(
  272. this,
  273. st::historyComposeField,
  274. Ui::InputField::Mode::MultiLine,
  275. tr::lng_message_ph())
  276. , _kbScroll(this, st::botKbScroll)
  277. , _keyboard(_kbScroll->setOwnedWidget(object_ptr<BotKeyboard>(
  278. controller,
  279. this)))
  280. , _membersDropdownShowTimer([=] { showMembersDropdown(); })
  281. , _highlighter(
  282. &session().data(),
  283. [=](const HistoryItem *item) { return item->mainView(); },
  284. [=](const HistoryView::Element *view) {
  285. session().data().requestViewRepaint(view);
  286. })
  287. , _saveDraftTimer([=] { saveDraft(); })
  288. , _saveCloudDraftTimer([=] { saveCloudDraft(); })
  289. , _paidReactionToast(std::make_unique<HistoryView::PaidReactionToast>(
  290. this,
  291. &session().data(),
  292. rpl::single(st::topBarHeight),
  293. [=](not_null<const HistoryView::Element*> view) {
  294. return _list && _list->itemTop(view) >= 0;
  295. }))
  296. , _topShadow(this) {
  297. setAcceptDrops(true);
  298. session().downloaderTaskFinished() | rpl::start_with_next([=] {
  299. update();
  300. }, lifetime());
  301. base::install_event_filter(_scroll.data(), [=](not_null<QEvent*> e) {
  302. const auto consumed = (e->type() == QEvent::Wheel)
  303. && _list
  304. && _list->consumeScrollAction(
  305. Ui::ScrollDelta(static_cast<QWheelEvent*>(e.get())));
  306. return consumed
  307. ? base::EventFilterResult::Cancel
  308. : base::EventFilterResult::Continue;
  309. });
  310. _scroll->scrolls() | rpl::start_with_next([=] {
  311. handleScroll();
  312. }, lifetime());
  313. _scroll->geometryChanged(
  314. ) | rpl::start_with_next(crl::guard(_list, [=] {
  315. _list->onParentGeometryChanged();
  316. }), lifetime());
  317. _scroll->addContentRequests(
  318. ) | rpl::start_with_next([=] {
  319. if (_history && _history->loadedAtBottom()) {
  320. using Result = Data::SponsoredMessages::AppendResult;
  321. const auto tryToAppend = [=] {
  322. const auto sponsored = &session().sponsoredMessages();
  323. const auto result = sponsored->append(_history);
  324. if (result == Result::Appended) {
  325. _scroll->contentAdded();
  326. }
  327. return result;
  328. };
  329. if (tryToAppend() == Result::MediaLoading
  330. && !_historySponsoredPreloading) {
  331. session().downloaderTaskFinished(
  332. ) | rpl::start_with_next([=] {
  333. if (tryToAppend() != Result::MediaLoading) {
  334. _historySponsoredPreloading.destroy();
  335. }
  336. }, _historySponsoredPreloading);
  337. }
  338. }
  339. }, lifetime());
  340. _fieldBarCancel->addClickHandler([=] { cancelFieldAreaState(); });
  341. _send->addClickHandler([=] { sendButtonClicked(); });
  342. _mediaEditManager.updateRequests() | rpl::start_with_next([this] {
  343. updateOverStates(mapFromGlobal(QCursor::pos()));
  344. }, lifetime());
  345. {
  346. using namespace SendMenu;
  347. const auto sendAction = [=](Action action, Details) {
  348. if (action.type == ActionType::CaptionUp
  349. || action.type == ActionType::CaptionDown
  350. || action.type == ActionType::SpoilerOn
  351. || action.type == ActionType::SpoilerOff) {
  352. _mediaEditManager.apply(action);
  353. } else if (action.type == ActionType::Send) {
  354. send(action.options);
  355. } else {
  356. sendScheduled(action.options);
  357. }
  358. };
  359. SetupMenuAndShortcuts(
  360. _send.get(),
  361. controller->uiShow(),
  362. [=] { return sendButtonMenuDetails(); },
  363. sendAction);
  364. }
  365. _unblock->addClickHandler([=] { unblockUser(); });
  366. _botStart->addClickHandler([=] { sendBotStartCommand(); });
  367. _joinChannel->addClickHandler([=] { joinChannel(); });
  368. _muteUnmute->addClickHandler([=] { toggleMuteUnmute(); });
  369. setupGiftToChannelButton();
  370. _reportMessages->addClickHandler([=] { reportSelectedMessages(); });
  371. _field->submits(
  372. ) | rpl::start_with_next([=](Qt::KeyboardModifiers modifiers) {
  373. sendWithModifiers(modifiers);
  374. }, _field->lifetime());
  375. _field->cancelled(
  376. ) | rpl::start_with_next([=] {
  377. escape();
  378. }, _field->lifetime());
  379. _field->tabbed(
  380. ) | rpl::start_with_next([=] {
  381. fieldTabbed();
  382. }, _field->lifetime());
  383. _field->heightChanges(
  384. ) | rpl::start_with_next([=] {
  385. fieldResized();
  386. }, _field->lifetime());
  387. _field->focusedChanges(
  388. ) | rpl::filter(rpl::mappers::_1) | rpl::start_with_next([=] {
  389. fieldFocused();
  390. }, _field->lifetime());
  391. _field->changes(
  392. ) | rpl::start_with_next([=] {
  393. fieldChanged();
  394. }, _field->lifetime());
  395. #ifdef Q_OS_MAC
  396. // Removed an ability to insert text from the menu bar
  397. // when the field is hidden.
  398. _field->shownValue(
  399. ) | rpl::start_with_next([=](bool shown) {
  400. _field->setEnabled(shown);
  401. }, _field->lifetime());
  402. #endif // Q_OS_MAC
  403. controller->widget()->shownValue(
  404. ) | rpl::skip(1) | rpl::start_with_next([=] {
  405. windowIsVisibleChanged();
  406. }, lifetime());
  407. initTabbedSelector();
  408. _attachToggle->setClickedCallback([=] {
  409. const auto toggle = _attachBotsMenu && _attachBotsMenu->isHidden();
  410. base::call_delayed(st::historyAttach.ripple.hideDuration, this, [=] {
  411. if (_attachBotsMenu && toggle) {
  412. _attachBotsMenu->showAnimated();
  413. } else {
  414. chooseAttach();
  415. if (_attachBotsMenu) {
  416. _attachBotsMenu->hideAnimated();
  417. }
  418. }
  419. });
  420. });
  421. const auto rawTextEdit = _field->rawTextEdit().get();
  422. rpl::merge(
  423. _field->scrollTop().changes() | rpl::to_empty,
  424. base::qt_signal_producer(
  425. rawTextEdit,
  426. &QTextEdit::cursorPositionChanged)
  427. ) | rpl::start_with_next([=] {
  428. saveDraftDelayed();
  429. }, _field->lifetime());
  430. _fieldBarCancel->hide();
  431. _topBar->hide();
  432. _scroll->hide();
  433. _kbScroll->hide();
  434. controller->chatStyle()->paletteChanged(
  435. ) | rpl::start_with_next([=] {
  436. _scroll->updateBars();
  437. }, lifetime());
  438. _forwardPanel->itemsUpdated(
  439. ) | rpl::start_with_next([=] {
  440. updateControlsVisibility();
  441. updateControlsGeometry();
  442. }, lifetime());
  443. InitMessageField(controller, _field, [=](
  444. not_null<DocumentData*> document) {
  445. if (_peer && Data::AllowEmojiWithoutPremium(_peer, document)) {
  446. return true;
  447. }
  448. showPremiumToast(document);
  449. return false;
  450. });
  451. InitMessageFieldFade(_field, st::historyComposeField.textBg);
  452. setupFastButtonMode();
  453. _fieldCharsCountManager.limitExceeds(
  454. ) | rpl::start_with_next([=] {
  455. const auto hide = _fieldCharsCountManager.isLimitExceeded();
  456. if (_silent) {
  457. _silent->setVisible(!hide);
  458. }
  459. if (_ttlInfo) {
  460. _ttlInfo->setVisible(!hide);
  461. }
  462. if (_scheduled) {
  463. _scheduled->setVisible(!hide);
  464. }
  465. updateFieldSize();
  466. moveFieldControls();
  467. }, lifetime());
  468. _send->widthValue() | rpl::skip(1) | rpl::start_with_next([=] {
  469. updateFieldSize();
  470. moveFieldControls();
  471. }, _send->lifetime());
  472. _keyboard->sendCommandRequests(
  473. ) | rpl::start_with_next([=](Bot::SendCommandRequest r) {
  474. sendBotCommand(r);
  475. }, lifetime());
  476. if (_supportAutocomplete) {
  477. supportInitAutocomplete();
  478. }
  479. _field->rawTextEdit()->installEventFilter(this);
  480. _field->setMimeDataHook([=](
  481. not_null<const QMimeData*> data,
  482. Ui::InputField::MimeAction action) {
  483. if (action == Ui::InputField::MimeAction::Check) {
  484. return canSendFiles(data);
  485. } else if (action == Ui::InputField::MimeAction::Insert) {
  486. return confirmSendingFiles(
  487. data,
  488. std::nullopt,
  489. Core::ReadMimeText(data));
  490. }
  491. Unexpected("action in MimeData hook.");
  492. });
  493. updateFieldSubmitSettings();
  494. _field->hide();
  495. _send->hide();
  496. _unblock->hide();
  497. _botStart->hide();
  498. _joinChannel->hide();
  499. _muteUnmute->hide();
  500. _reportMessages->hide();
  501. initVoiceRecordBar();
  502. _attachToggle->hide();
  503. _tabbedSelectorToggle->hide();
  504. _botKeyboardShow->hide();
  505. _botKeyboardHide->hide();
  506. _botCommandStart->hide();
  507. session().attachWebView().requestBots();
  508. rpl::merge(
  509. session().attachWebView().attachBotsUpdates(),
  510. session().changes().peerUpdates(
  511. Data::PeerUpdate::Flag::Rights
  512. | Data::PeerUpdate::Flag::StarsPerMessage
  513. ) | rpl::filter([=](const Data::PeerUpdate &update) {
  514. return update.peer == _peer;
  515. }) | rpl::to_empty
  516. ) | rpl::start_with_next([=] {
  517. refreshAttachBotsMenu();
  518. }, lifetime());
  519. _botKeyboardShow->addClickHandler([=] { toggleKeyboard(); });
  520. _botKeyboardHide->addClickHandler([=] { toggleKeyboard(); });
  521. _botCommandStart->addClickHandler([=] { startBotCommand(); });
  522. _topShadow->hide();
  523. _attachDragAreas = DragArea::SetupDragAreaToContainer(
  524. this,
  525. crl::guard(this, [=](not_null<const QMimeData*> d) {
  526. if (!_peer || isRecording()) {
  527. return false;
  528. }
  529. const auto topic = resolveReplyToTopic();
  530. return topic
  531. ? Data::CanSendAnyOf(topic, Data::FilesSendRestrictions())
  532. : Data::CanSendAnyOf(_peer, Data::FilesSendRestrictions());
  533. }),
  534. crl::guard(this, [=](bool f) { _field->setAcceptDrops(f); }),
  535. crl::guard(this, [=] { updateControlsGeometry(); }));
  536. _attachDragAreas.document->setDroppedCallback([=](const QMimeData *data) {
  537. confirmSendingFiles(data, false);
  538. Window::ActivateWindow(controller);
  539. });
  540. _attachDragAreas.photo->setDroppedCallback([=](const QMimeData *data) {
  541. confirmSendingFiles(data, true);
  542. Window::ActivateWindow(controller);
  543. });
  544. Core::App().mediaDevices().recordAvailabilityValue(
  545. ) | rpl::start_with_next([=](Webrtc::RecordAvailability value) {
  546. _recordAvailability = value;
  547. if (_list) {
  548. updateSendButtonType();
  549. }
  550. }, lifetime());
  551. session().data().newItemAdded(
  552. ) | rpl::start_with_next([=](not_null<HistoryItem*> item) {
  553. newItemAdded(item);
  554. }, lifetime());
  555. session().data().historyChanged(
  556. ) | rpl::start_with_next([=](not_null<History*> history) {
  557. handleHistoryChange(history);
  558. }, lifetime());
  559. session().data().viewResizeRequest(
  560. ) | rpl::start_with_next([=](not_null<HistoryView::Element*> view) {
  561. const auto item = view->data();
  562. const auto history = item->history();
  563. if (item->mainView() == view
  564. && (history == _history || history == _migrated)) {
  565. updateHistoryGeometry();
  566. }
  567. }, lifetime());
  568. session().data().itemDataChanges(
  569. ) | rpl::filter([=](not_null<HistoryItem*> item) {
  570. return !_list && (item->mainView() != nullptr);
  571. }) | rpl::start_with_next([=](not_null<HistoryItem*> item) {
  572. item->mainView()->itemDataChanged();
  573. }, lifetime());
  574. Core::App().settings().largeEmojiChanges(
  575. ) | rpl::start_with_next([=] {
  576. crl::on_main(this, [=] {
  577. updateHistoryGeometry();
  578. });
  579. }, lifetime());
  580. Core::App().settings().sendSubmitWayValue(
  581. ) | rpl::start_with_next([=] {
  582. crl::on_main(this, [=] {
  583. updateFieldSubmitSettings();
  584. });
  585. }, lifetime());
  586. session().data().channelDifferenceTooLong(
  587. ) | rpl::filter([=](not_null<ChannelData*> channel) {
  588. return _peer == channel.get();
  589. }) | rpl::start_with_next([=] {
  590. _cornerButtons.updateJumpDownVisibility();
  591. preloadHistoryIfNeeded();
  592. }, lifetime());
  593. session().data().userIsBotChanges(
  594. ) | rpl::filter([=](not_null<UserData*> user) {
  595. return (_peer == user.get());
  596. }) | rpl::start_with_next([=](not_null<UserData*> user) {
  597. _list->refreshAboutView();
  598. _list->updateBotInfo();
  599. updateControlsVisibility();
  600. updateControlsGeometry();
  601. }, lifetime());
  602. session().data().botCommandsChanges(
  603. ) | rpl::filter([=](not_null<PeerData*> peer) {
  604. return _peer && (_peer == peer);
  605. }) | rpl::start_with_next([=] {
  606. if (updateCmdStartShown()) {
  607. updateControlsVisibility();
  608. updateControlsGeometry();
  609. }
  610. }, lifetime());
  611. using EntryUpdateFlag = Data::EntryUpdate::Flag;
  612. session().changes().entryUpdates(
  613. EntryUpdateFlag::HasPinnedMessages
  614. | EntryUpdateFlag::ForwardDraft
  615. ) | rpl::start_with_next([=](const Data::EntryUpdate &update) {
  616. if (_pinnedTracker
  617. && (update.flags & EntryUpdateFlag::HasPinnedMessages)
  618. && ((update.entry.get() == _history)
  619. || (update.entry.get() == _migrated))) {
  620. checkPinnedBarState();
  621. }
  622. if (update.flags & EntryUpdateFlag::ForwardDraft) {
  623. updateForwarding();
  624. }
  625. }, lifetime());
  626. using HistoryUpdateFlag = Data::HistoryUpdate::Flag;
  627. session().changes().historyUpdates(
  628. HistoryUpdateFlag::MessageSent
  629. | HistoryUpdateFlag::BotKeyboard
  630. | HistoryUpdateFlag::CloudDraft
  631. | HistoryUpdateFlag::UnreadMentions
  632. | HistoryUpdateFlag::UnreadReactions
  633. | HistoryUpdateFlag::UnreadView
  634. | HistoryUpdateFlag::TopPromoted
  635. | HistoryUpdateFlag::ClientSideMessages
  636. ) | rpl::filter([=](const Data::HistoryUpdate &update) {
  637. return (_history == update.history.get());
  638. }) | rpl::start_with_next([=](const Data::HistoryUpdate &update) {
  639. const auto flags = update.flags;
  640. if (flags & HistoryUpdateFlag::MessageSent) {
  641. synteticScrollToY(_scroll->scrollTopMax());
  642. }
  643. if (flags & HistoryUpdateFlag::BotKeyboard) {
  644. updateBotKeyboard(update.history);
  645. }
  646. if (flags & HistoryUpdateFlag::CloudDraft) {
  647. applyCloudDraft(update.history);
  648. }
  649. if (flags & HistoryUpdateFlag::ClientSideMessages) {
  650. updateSendButtonType();
  651. }
  652. if ((flags & HistoryUpdateFlag::UnreadMentions)
  653. || (flags & HistoryUpdateFlag::UnreadReactions)) {
  654. _cornerButtons.updateUnreadThingsVisibility();
  655. }
  656. if (flags & HistoryUpdateFlag::UnreadView) {
  657. unreadCountUpdated();
  658. }
  659. if (flags & HistoryUpdateFlag::TopPromoted) {
  660. updateHistoryGeometry();
  661. updateControlsVisibility();
  662. updateControlsGeometry();
  663. this->update();
  664. }
  665. }, lifetime());
  666. using MessageUpdateFlag = Data::MessageUpdate::Flag;
  667. session().changes().messageUpdates(
  668. MessageUpdateFlag::Destroyed
  669. | MessageUpdateFlag::Edited
  670. | MessageUpdateFlag::ReplyMarkup
  671. | MessageUpdateFlag::BotCallbackSent
  672. ) | rpl::start_with_next([=](const Data::MessageUpdate &update) {
  673. const auto flags = update.flags;
  674. if (flags & MessageUpdateFlag::Destroyed) {
  675. itemRemoved(update.item);
  676. return;
  677. }
  678. if (flags & MessageUpdateFlag::Edited) {
  679. itemEdited(update.item);
  680. }
  681. if (flags & MessageUpdateFlag::ReplyMarkup) {
  682. if (_keyboard->forMsgId() == update.item->fullId()) {
  683. updateBotKeyboard(update.item->history(), true);
  684. }
  685. }
  686. if (flags & MessageUpdateFlag::BotCallbackSent) {
  687. botCallbackSent(update.item);
  688. }
  689. }, lifetime());
  690. session().changes().realtimeMessageUpdates(
  691. MessageUpdateFlag::NewUnreadReaction
  692. ) | rpl::start_with_next([=](const Data::MessageUpdate &update) {
  693. maybeMarkReactionsRead(update.item);
  694. }, lifetime());
  695. session().data().sentToScheduled(
  696. ) | rpl::start_with_next([=](const Data::SentToScheduled &value) {
  697. const auto history = value.history;
  698. if (history == _history) {
  699. const auto id = value.scheduledId;
  700. crl::on_main(this, [=] {
  701. if (history == _history) {
  702. controller->showSection(
  703. std::make_shared<HistoryView::ScheduledMemento>(
  704. history,
  705. id));
  706. }
  707. });
  708. return;
  709. }
  710. }, lifetime());
  711. session().data().sentFromScheduled(
  712. ) | rpl::start_with_next([=](const Data::SentFromScheduled &value) {
  713. if (value.item->awaitingVideoProcessing()
  714. && !_sentFromScheduledTip
  715. && HistoryView::ShowScheduledVideoPublished(
  716. controller,
  717. value,
  718. crl::guard(this, [=] { _sentFromScheduledTip = false; }))) {
  719. _sentFromScheduledTip = true;
  720. }
  721. }, lifetime());
  722. using MediaSwitch = Media::Player::Instance::Switch;
  723. Media::Player::instance()->switchToNextEvents(
  724. ) | rpl::filter([=](const MediaSwitch &pair) {
  725. return (pair.from.type() == AudioMsgId::Type::Voice);
  726. }) | rpl::start_with_next([=](const MediaSwitch &pair) {
  727. scrollToCurrentVoiceMessage(pair.from.contextId(), pair.to);
  728. }, lifetime());
  729. session().user()->flagsValue(
  730. ) | rpl::start_with_next([=](UserData::Flags::Change change) {
  731. if (change.diff & UserData::Flag::Premium) {
  732. if (const auto user = _peer ? _peer->asUser() : nullptr) {
  733. if (user->requiresPremiumToWrite()) {
  734. handlePeerUpdate();
  735. }
  736. }
  737. }
  738. }, lifetime());
  739. using PeerUpdateFlag = Data::PeerUpdate::Flag;
  740. session().changes().peerUpdates(
  741. PeerUpdateFlag::Rights
  742. | PeerUpdateFlag::Migration
  743. | PeerUpdateFlag::UnavailableReason
  744. | PeerUpdateFlag::IsBlocked
  745. | PeerUpdateFlag::Admins
  746. | PeerUpdateFlag::Members
  747. | PeerUpdateFlag::OnlineStatus
  748. | PeerUpdateFlag::Notifications
  749. | PeerUpdateFlag::ChannelAmIn
  750. | PeerUpdateFlag::ChannelLinkedChat
  751. | PeerUpdateFlag::Slowmode
  752. | PeerUpdateFlag::BotStartToken
  753. | PeerUpdateFlag::MessagesTTL
  754. | PeerUpdateFlag::ChatThemeEmoji
  755. | PeerUpdateFlag::FullInfo
  756. | PeerUpdateFlag::StarsPerMessage
  757. ) | rpl::filter([=](const Data::PeerUpdate &update) {
  758. return (update.peer.get() == _peer);
  759. }) | rpl::map([](const Data::PeerUpdate &update) {
  760. return update.flags;
  761. }) | rpl::start_with_next([=](Data::PeerUpdate::Flags flags) {
  762. if (flags & PeerUpdateFlag::Rights) {
  763. updateFieldPlaceholder();
  764. updateSendButtonType();
  765. _preview->checkNow(false);
  766. const auto was = (_sendAs != nullptr);
  767. refreshSendAsToggle();
  768. if (was != (_sendAs != nullptr)) {
  769. updateControlsVisibility();
  770. updateControlsGeometry();
  771. orderWidgets();
  772. }
  773. }
  774. if (flags & PeerUpdateFlag::Migration) {
  775. handlePeerMigration();
  776. }
  777. if (flags & PeerUpdateFlag::Notifications) {
  778. updateNotifyControls();
  779. }
  780. if (flags & PeerUpdateFlag::UnavailableReason) {
  781. const auto unavailable = _peer->computeUnavailableReason();
  782. if (!unavailable.isEmpty()) {
  783. const auto account = not_null(&_peer->account());
  784. closeCurrent();
  785. if (const auto primary = Core::App().windowFor(account)) {
  786. primary->showToast(unavailable);
  787. }
  788. return;
  789. }
  790. }
  791. if (flags & PeerUpdateFlag::StarsPerMessage) {
  792. updateFieldPlaceholder();
  793. updateSendButtonType();
  794. }
  795. if (flags & PeerUpdateFlag::BotStartToken) {
  796. updateControlsVisibility();
  797. updateControlsGeometry();
  798. }
  799. if (flags & PeerUpdateFlag::Slowmode) {
  800. updateSendButtonType();
  801. }
  802. if (flags & (PeerUpdateFlag::IsBlocked
  803. | PeerUpdateFlag::Admins
  804. | PeerUpdateFlag::Members
  805. | PeerUpdateFlag::OnlineStatus
  806. | PeerUpdateFlag::Rights
  807. | PeerUpdateFlag::ChannelAmIn
  808. | PeerUpdateFlag::ChannelLinkedChat)) {
  809. handlePeerUpdate();
  810. }
  811. if (flags & PeerUpdateFlag::MessagesTTL) {
  812. checkMessagesTTL();
  813. }
  814. if ((flags & PeerUpdateFlag::ChatThemeEmoji) && _list) {
  815. const auto emoji = _peer->themeEmoji();
  816. if (Data::CloudThemes::TestingColors() && !emoji.isEmpty()) {
  817. _peer->owner().cloudThemes().themeForEmojiValue(
  818. emoji
  819. ) | rpl::filter_optional(
  820. ) | rpl::take(
  821. 1
  822. ) | rpl::start_with_next([=](const Data::CloudTheme &theme) {
  823. const auto &themes = _peer->owner().cloudThemes();
  824. const auto text = themes.prepareTestingLink(theme);
  825. if (!text.isEmpty()) {
  826. _field->setText(text);
  827. }
  828. }, _list->lifetime());
  829. }
  830. }
  831. if (flags & PeerUpdateFlag::FullInfo) {
  832. fullInfoUpdated();
  833. if (_peer->starsPerMessageChecked()) {
  834. session().credits().load();
  835. } else if (const auto channel = _peer->asChannel()) {
  836. if (channel->allowedReactions().paidEnabled) {
  837. session().credits().load();
  838. }
  839. }
  840. }
  841. }, lifetime());
  842. using Type = Data::DefaultNotify;
  843. rpl::merge(
  844. session().data().notifySettings().defaultUpdates(Type::User),
  845. session().data().notifySettings().defaultUpdates(Type::Group),
  846. session().data().notifySettings().defaultUpdates(Type::Broadcast)
  847. ) | rpl::start_with_next([=] {
  848. updateNotifyControls();
  849. }, lifetime());
  850. session().data().itemVisibilityQueries(
  851. ) | rpl::filter([=](
  852. const Data::Session::ItemVisibilityQuery &query) {
  853. return !_showAnimation
  854. && (_history == query.item->history())
  855. && (query.item->mainView() != nullptr)
  856. && isVisible();
  857. }) | rpl::start_with_next([=](
  858. const Data::Session::ItemVisibilityQuery &query) {
  859. if (const auto view = query.item->mainView()) {
  860. auto top = _list->itemTop(view);
  861. if (top >= 0) {
  862. auto scrollTop = _scroll->scrollTop();
  863. if (top + view->height() > scrollTop
  864. && top < scrollTop + _scroll->height()) {
  865. *query.isVisible = true;
  866. }
  867. }
  868. }
  869. }, lifetime());
  870. _topBar->membersShowAreaActive(
  871. ) | rpl::start_with_next([=](bool active) {
  872. setMembersShowAreaActive(active);
  873. }, _topBar->lifetime());
  874. _topBar->forwardSelectionRequest(
  875. ) | rpl::start_with_next([=] {
  876. forwardSelected();
  877. }, _topBar->lifetime());
  878. _topBar->deleteSelectionRequest(
  879. ) | rpl::start_with_next([=] {
  880. confirmDeleteSelected();
  881. }, _topBar->lifetime());
  882. _topBar->clearSelectionRequest(
  883. ) | rpl::start_with_next([=] {
  884. clearSelected();
  885. }, _topBar->lifetime());
  886. _topBar->cancelChooseForReportRequest(
  887. ) | rpl::start_with_next([=] {
  888. setChooseReportMessagesDetails({}, nullptr);
  889. }, _topBar->lifetime());
  890. _topBar->searchRequest(
  891. ) | rpl::start_with_next([=] {
  892. if (_history) {
  893. controller->searchInChat(_history);
  894. }
  895. }, _topBar->lifetime());
  896. session().api().sendActions(
  897. ) | rpl::filter([=](const Api::SendAction &action) {
  898. return (action.history == _history);
  899. }) | rpl::start_with_next([=](const Api::SendAction &action) {
  900. const auto lastKeyboardUsed = lastForceReplyReplied(
  901. action.replyTo.messageId);
  902. if (action.replaceMediaOf) {
  903. } else if (action.options.scheduled) {
  904. cancelReply(lastKeyboardUsed);
  905. crl::on_main(this, [=, history = action.history] {
  906. controller->showSection(
  907. std::make_shared<HistoryView::ScheduledMemento>(history));
  908. });
  909. } else {
  910. fastShowAtEnd(action.history);
  911. if (!_justMarkingAsRead
  912. && cancelReply(lastKeyboardUsed)
  913. && !action.clearDraft) {
  914. saveCloudDraft();
  915. }
  916. }
  917. if (action.options.handleSupportSwitch) {
  918. handleSupportSwitch(action.history);
  919. }
  920. }, lifetime());
  921. if (session().supportMode()) {
  922. session().data().chatListEntryRefreshes(
  923. ) | rpl::start_with_next([=] {
  924. crl::on_main(this, [=] { checkSupportPreload(true); });
  925. }, lifetime());
  926. }
  927. Core::App().materializeLocalDraftsRequests(
  928. ) | rpl::start_with_next([=] {
  929. saveFieldToHistoryLocalDraft();
  930. }, lifetime());
  931. setupScheduledToggle();
  932. setupSendAsToggle();
  933. orderWidgets();
  934. setupShortcuts();
  935. }
  936. void HistoryWidget::setGeometryWithTopMoved(
  937. const QRect &newGeometry,
  938. int topDelta) {
  939. _topDelta = topDelta;
  940. bool willBeResized = (size() != newGeometry.size());
  941. if (geometry() != newGeometry) {
  942. auto weak = Ui::MakeWeak(this);
  943. setGeometry(newGeometry);
  944. if (!weak) {
  945. return;
  946. }
  947. }
  948. if (!willBeResized) {
  949. resizeEvent(nullptr);
  950. }
  951. _topDelta = 0;
  952. }
  953. Dialogs::EntryState HistoryWidget::computeDialogsEntryState() const {
  954. return Dialogs::EntryState{
  955. .key = _history,
  956. .section = Dialogs::EntryState::Section::History,
  957. .currentReplyTo = replyTo(),
  958. };
  959. }
  960. void HistoryWidget::refreshJoinChannelText() {
  961. if (const auto channel = _peer ? _peer->asChannel() : nullptr) {
  962. _joinChannel->setText((channel->isBroadcast()
  963. ? tr::lng_profile_join_channel(tr::now)
  964. : (channel->requestToJoin() && !channel->amCreator())
  965. ? tr::lng_profile_apply_to_join_group(tr::now)
  966. : tr::lng_profile_join_group(tr::now)).toUpper());
  967. }
  968. }
  969. void HistoryWidget::refreshGiftToChannelShown() {
  970. if (!_giftToChannelIn || !_giftToChannelOut) {
  971. return;
  972. }
  973. const auto channel = _peer->asChannel();
  974. const auto shown = channel
  975. && channel->isBroadcast()
  976. && channel->stargiftsAvailable();
  977. _giftToChannelIn->setVisible(shown);
  978. _giftToChannelOut->setVisible(shown);
  979. }
  980. void HistoryWidget::refreshTopBarActiveChat() {
  981. const auto state = computeDialogsEntryState();
  982. _topBar->setActiveChat(state, _history->sendActionPainter());
  983. if (state.key) {
  984. controller()->setDialogsEntryState(state);
  985. }
  986. }
  987. void HistoryWidget::refreshTabbedPanel() {
  988. if (_peer && controller()->hasTabbedSelectorOwnership()) {
  989. createTabbedPanel();
  990. } else {
  991. setTabbedPanel(nullptr);
  992. }
  993. }
  994. void HistoryWidget::initVoiceRecordBar() {
  995. _voiceRecordBar->setStartRecordingFilter([=] {
  996. const auto error = [&]() -> Data::SendError {
  997. if (_peer) {
  998. if (const auto error = Data::RestrictionError(
  999. _peer,
  1000. ChatRestriction::SendVoiceMessages)) {
  1001. return error;
  1002. }
  1003. }
  1004. return {};
  1005. }();
  1006. if (error) {
  1007. Data::ShowSendErrorToast(controller(), _peer, error);
  1008. return true;
  1009. } else if (showSlowmodeError()) {
  1010. return true;
  1011. }
  1012. return false;
  1013. });
  1014. _voiceRecordBar->setTTLFilter([=] {
  1015. if (const auto peer = _history ? _history->peer.get() : nullptr) {
  1016. if (const auto user = peer->asUser()) {
  1017. if (!user->isSelf() && !user->isBot()) {
  1018. return true;
  1019. }
  1020. }
  1021. }
  1022. return false;
  1023. });
  1024. const auto applyLocalDraft = [=] {
  1025. if (_history && _history->localDraft({})) {
  1026. applyDraft();
  1027. }
  1028. };
  1029. _voiceRecordBar->sendActionUpdates(
  1030. ) | rpl::start_with_next([=](const auto &data) {
  1031. if (!_history) {
  1032. return;
  1033. }
  1034. session().sendProgressManager().update(
  1035. _history,
  1036. data.type,
  1037. data.progress);
  1038. }, lifetime());
  1039. _voiceRecordBar->sendVoiceRequests(
  1040. ) | rpl::start_with_next([=](const VoiceToSend &data) {
  1041. sendVoice(data);
  1042. }, lifetime());
  1043. _voiceRecordBar->cancelRequests(
  1044. ) | rpl::start_with_next(applyLocalDraft, lifetime());
  1045. _voiceRecordBar->lockShowStarts(
  1046. ) | rpl::start_with_next([=] {
  1047. _cornerButtons.updateJumpDownVisibility();
  1048. _cornerButtons.updateUnreadThingsVisibility();
  1049. }, lifetime());
  1050. _voiceRecordBar->errors(
  1051. ) | rpl::start_with_next([=](::Media::Capture::Error error) {
  1052. using Error = ::Media::Capture::Error;
  1053. switch (error) {
  1054. case Error::AudioInit:
  1055. case Error::AudioTimeout:
  1056. controller()->showToast(tr::lng_record_audio_problem(tr::now));
  1057. break;
  1058. case Error::VideoInit:
  1059. case Error::VideoTimeout:
  1060. controller()->showToast(tr::lng_record_video_problem(tr::now));
  1061. break;
  1062. default:
  1063. controller()->showToast(u"Unknown error."_q);
  1064. break;
  1065. }
  1066. }, lifetime());
  1067. _voiceRecordBar->updateSendButtonTypeRequests(
  1068. ) | rpl::start_with_next([=] {
  1069. updateSendButtonType();
  1070. }, lifetime());
  1071. _voiceRecordBar->lockViewportEvents(
  1072. ) | rpl::start_with_next([=](not_null<QEvent*> e) {
  1073. _scroll->viewportEvent(e);
  1074. }, lifetime());
  1075. _voiceRecordBar->recordingTipRequests(
  1076. ) | rpl::start_with_next([=] {
  1077. Core::App().settings().setRecordVideoMessages(
  1078. !Core::App().settings().recordVideoMessages());
  1079. updateSendButtonType();
  1080. switch (_send->type()) {
  1081. case Ui::SendButton::Type::Record: {
  1082. const auto can = Webrtc::RecordAvailability::VideoAndAudio;
  1083. controller()->showToast((_recordAvailability == can)
  1084. ? tr::lng_record_voice_tip(tr::now)
  1085. : tr::lng_record_hold_tip(tr::now));
  1086. } break;
  1087. case Ui::SendButton::Type::Round:
  1088. controller()->showToast(tr::lng_record_video_tip(tr::now));
  1089. break;
  1090. }
  1091. }, lifetime());
  1092. _voiceRecordBar->recordingStateChanges(
  1093. ) | rpl::start_with_next([=](bool active) {
  1094. controller()->widget()->setInnerFocus();
  1095. }, lifetime());
  1096. _voiceRecordBar->hideFast();
  1097. }
  1098. void HistoryWidget::initTabbedSelector() {
  1099. refreshTabbedPanel();
  1100. _tabbedSelectorToggle->addClickHandler([=] {
  1101. if (_tabbedPanel && _tabbedPanel->isHidden()) {
  1102. _tabbedPanel->showAnimated();
  1103. } else {
  1104. toggleTabbedSelectorMode();
  1105. }
  1106. });
  1107. const auto selector = controller()->tabbedSelector();
  1108. base::install_event_filter(this, selector, [=](not_null<QEvent*> e) {
  1109. if (_tabbedPanel && e->type() == QEvent::ParentChange) {
  1110. setTabbedPanel(nullptr);
  1111. }
  1112. return base::EventFilterResult::Continue;
  1113. });
  1114. auto filter = rpl::filter([=] {
  1115. return !isHidden();
  1116. });
  1117. using Selector = TabbedSelector;
  1118. selector->emojiChosen(
  1119. ) | rpl::filter([=] {
  1120. return !isHidden() && !_field->isHidden();
  1121. }) | rpl::start_with_next([=](ChatHelpers::EmojiChosen data) {
  1122. Ui::InsertEmojiAtCursor(_field->textCursor(), data.emoji);
  1123. }, lifetime());
  1124. rpl::merge(
  1125. selector->fileChosen() | filter,
  1126. selector->customEmojiChosen() | filter,
  1127. controller()->stickerOrEmojiChosen() | filter
  1128. ) | rpl::start_with_next([=](ChatHelpers::FileChosen &&data) {
  1129. fileChosen(std::move(data));
  1130. }, lifetime());
  1131. selector->photoChosen(
  1132. ) | filter | rpl::start_with_next([=](ChatHelpers::PhotoChosen data) {
  1133. sendExistingPhoto(data.photo, data.options);
  1134. }, lifetime());
  1135. selector->inlineResultChosen(
  1136. ) | filter | rpl::filter([=](const ChatHelpers::InlineChosen &data) {
  1137. if (!data.recipientOverride) {
  1138. return true;
  1139. } else if (data.recipientOverride != _peer) {
  1140. showHistory(data.recipientOverride->id, ShowAtTheEndMsgId);
  1141. }
  1142. return (data.recipientOverride == _peer);
  1143. }) | rpl::start_with_next([=](ChatHelpers::InlineChosen data) {
  1144. sendInlineResult(data);
  1145. }, lifetime());
  1146. selector->contextMenuRequested(
  1147. ) | filter | rpl::start_with_next([=] {
  1148. selector->showMenuWithDetails(sendMenuDetails());
  1149. }, lifetime());
  1150. selector->choosingStickerUpdated(
  1151. ) | rpl::start_with_next([=](const Selector::Action &data) {
  1152. if (!_history) {
  1153. return;
  1154. }
  1155. const auto type = Api::SendProgressType::ChooseSticker;
  1156. if (data != Selector::Action::Cancel) {
  1157. session().sendProgressManager().update(_history, type);
  1158. } else {
  1159. session().sendProgressManager().cancel(_history, type);
  1160. }
  1161. }, lifetime());
  1162. }
  1163. void HistoryWidget::supportInitAutocomplete() {
  1164. _supportAutocomplete->hide();
  1165. _supportAutocomplete->insertRequests(
  1166. ) | rpl::start_with_next([=](const QString &text) {
  1167. supportInsertText(text);
  1168. }, _supportAutocomplete->lifetime());
  1169. _supportAutocomplete->shareContactRequests(
  1170. ) | rpl::start_with_next([=](const Support::Contact &contact) {
  1171. supportShareContact(contact);
  1172. }, _supportAutocomplete->lifetime());
  1173. }
  1174. void HistoryWidget::supportInsertText(const QString &text) {
  1175. _field->setFocus();
  1176. _field->textCursor().insertText(text);
  1177. _field->ensureCursorVisible();
  1178. }
  1179. void HistoryWidget::supportShareContact(Support::Contact contact) {
  1180. if (!_history) {
  1181. return;
  1182. }
  1183. supportInsertText(contact.comment);
  1184. contact.comment = _field->getLastText();
  1185. const auto submit = [=](Qt::KeyboardModifiers modifiers) {
  1186. const auto history = _history;
  1187. if (!history) {
  1188. return;
  1189. }
  1190. auto options = Api::SendOptions{
  1191. .sendAs = prepareSendAction({}).options.sendAs,
  1192. };
  1193. auto action = Api::SendAction(history);
  1194. send(options);
  1195. options.handleSupportSwitch = Support::HandleSwitch(modifiers);
  1196. action.options = options;
  1197. session().api().shareContact(
  1198. contact.phone,
  1199. contact.firstName,
  1200. contact.lastName,
  1201. action);
  1202. };
  1203. const auto box = controller()->show(Box<Support::ConfirmContactBox>(
  1204. controller(),
  1205. _history,
  1206. contact,
  1207. crl::guard(this, submit)));
  1208. box->boxClosing(
  1209. ) | rpl::start_with_next([=] {
  1210. _field->document()->undo();
  1211. }, lifetime());
  1212. }
  1213. void HistoryWidget::scrollToCurrentVoiceMessage(
  1214. FullMsgId fromId,
  1215. FullMsgId toId) {
  1216. if (crl::now() <= _lastUserScrolled + kScrollToVoiceAfterScrolledMs) {
  1217. return;
  1218. }
  1219. if (!_list) {
  1220. return;
  1221. }
  1222. auto from = session().data().message(fromId);
  1223. auto to = session().data().message(toId);
  1224. if (!from || !to) {
  1225. return;
  1226. }
  1227. // If history has pending resize items, the scrollTopItem won't be updated.
  1228. // And the scrollTop will be reset back to scrollTopItem + scrollTopOffset.
  1229. handlePendingHistoryUpdate();
  1230. if (const auto toView = to->mainView()) {
  1231. auto toTop = _list->itemTop(toView);
  1232. if (toTop >= 0 && !isItemCompletelyHidden(from)) {
  1233. auto scrollTop = _scroll->scrollTop();
  1234. auto scrollBottom = scrollTop + _scroll->height();
  1235. auto toBottom = toTop + toView->height();
  1236. if ((toTop < scrollTop && toBottom < scrollBottom)
  1237. || (toTop > scrollTop && toBottom > scrollBottom)) {
  1238. animatedScrollToItem(to->id);
  1239. }
  1240. }
  1241. }
  1242. }
  1243. void HistoryWidget::animatedScrollToItem(MsgId msgId) {
  1244. Expects(_history != nullptr);
  1245. if (hasPendingResizedItems()) {
  1246. updateListSize();
  1247. }
  1248. auto to = session().data().message(_history->peer, msgId);
  1249. if (_list->itemTop(to) < 0) {
  1250. return;
  1251. }
  1252. auto scrollTo = std::clamp(
  1253. itemTopForHighlight(to->mainView()),
  1254. 0,
  1255. _scroll->scrollTopMax());
  1256. animatedScrollToY(scrollTo, to);
  1257. }
  1258. void HistoryWidget::animatedScrollToY(int scrollTo, HistoryItem *attachTo) {
  1259. Expects(_history != nullptr);
  1260. if (hasPendingResizedItems()) {
  1261. updateListSize();
  1262. }
  1263. // Attach our scroll animation to some item.
  1264. auto itemTop = _list->itemTop(attachTo);
  1265. auto scrollTop = _scroll->scrollTop();
  1266. if (itemTop < 0 && !_history->isEmpty()) {
  1267. attachTo = _history->blocks.back()->messages.back()->data();
  1268. itemTop = _list->itemTop(attachTo);
  1269. }
  1270. if (itemTop < 0 || (scrollTop == scrollTo)) {
  1271. synteticScrollToY(scrollTo);
  1272. return;
  1273. }
  1274. _scrollToAnimation.stop();
  1275. auto maxAnimatedDelta = _scroll->height();
  1276. auto transition = anim::sineInOut;
  1277. if (scrollTo > scrollTop + maxAnimatedDelta) {
  1278. scrollTop = scrollTo - maxAnimatedDelta;
  1279. synteticScrollToY(scrollTop);
  1280. transition = anim::easeOutCubic;
  1281. } else if (scrollTo + maxAnimatedDelta < scrollTop) {
  1282. scrollTop = scrollTo + maxAnimatedDelta;
  1283. synteticScrollToY(scrollTop);
  1284. transition = anim::easeOutCubic;
  1285. } else {
  1286. // In local showHistory() we forget current scroll state,
  1287. // so we need to restore it synchronously, otherwise we may
  1288. // jump to the bottom of history in some updateHistoryGeometry() call.
  1289. synteticScrollToY(scrollTop);
  1290. }
  1291. const auto itemId = attachTo->fullId();
  1292. const auto relativeFrom = scrollTop - itemTop;
  1293. const auto relativeTo = scrollTo - itemTop;
  1294. _scrollToAnimation.start(
  1295. [=] { scrollToAnimationCallback(itemId, relativeTo); },
  1296. relativeFrom,
  1297. relativeTo,
  1298. st::slideDuration,
  1299. anim::sineInOut);
  1300. }
  1301. void HistoryWidget::scrollToAnimationCallback(
  1302. FullMsgId attachToId,
  1303. int relativeTo) {
  1304. auto itemTop = _list->itemTop(session().data().message(attachToId));
  1305. if (itemTop < 0) {
  1306. _scrollToAnimation.stop();
  1307. } else {
  1308. synteticScrollToY(qRound(_scrollToAnimation.value(relativeTo))
  1309. + itemTop);
  1310. }
  1311. if (!_scrollToAnimation.animating()) {
  1312. preloadHistoryByScroll();
  1313. checkReplyReturns();
  1314. }
  1315. }
  1316. void HistoryWidget::enqueueMessageHighlight(
  1317. const HistoryView::SelectedQuote &quote) {
  1318. _highlighter.enqueue(quote);
  1319. }
  1320. Ui::ChatPaintHighlight HistoryWidget::itemHighlight(
  1321. not_null<const HistoryItem*> item) const {
  1322. return _highlighter.state(item);
  1323. }
  1324. int HistoryWidget::itemTopForHighlight(
  1325. not_null<HistoryView::Element*> view) const {
  1326. if (const auto group = session().data().groups().find(view->data())) {
  1327. if (const auto leader = group->items.front()->mainView()) {
  1328. view = leader;
  1329. }
  1330. }
  1331. const auto itemTop = _list->itemTop(view);
  1332. Assert(itemTop >= 0);
  1333. const auto item = view->data();
  1334. const auto unwatchedEffect = item->hasUnwatchedEffect();
  1335. const auto showReactions = item->hasUnreadReaction() || unwatchedEffect;
  1336. const auto reactionCenter = showReactions
  1337. ? view->reactionButtonParameters({}, {}).center.y()
  1338. : -1;
  1339. const auto visibleAreaHeight = _scroll->height();
  1340. const auto viewHeight = view->height();
  1341. const auto heightLeft = (visibleAreaHeight - viewHeight);
  1342. if (heightLeft >= 0) {
  1343. return std::max(itemTop - (heightLeft / 2), 0);
  1344. } else if (const auto sel = itemHighlight(item).range
  1345. ; !sel.empty() && !IsSubGroupSelection(sel)) {
  1346. const auto single = st::messageTextStyle.font->height;
  1347. const auto begin = HistoryView::FindViewY(view, sel.from) - single;
  1348. const auto end = HistoryView::FindViewY(view, sel.to, begin + single)
  1349. + 2 * single;
  1350. auto result = itemTop;
  1351. if (end > visibleAreaHeight) {
  1352. result = std::max(result, itemTop + end - visibleAreaHeight);
  1353. }
  1354. if (itemTop + begin < result) {
  1355. result = itemTop + begin;
  1356. }
  1357. return result;
  1358. } else if (reactionCenter >= 0) {
  1359. const auto maxSize = st::reactionInlineImage;
  1360. // Show message right till the bottom.
  1361. const auto forBottom = itemTop + viewHeight - visibleAreaHeight;
  1362. // Show message bottom and some space below for the effect.
  1363. const auto bottomResult = forBottom + maxSize;
  1364. // Show the reaction button center in the middle.
  1365. const auto byReactionResult = itemTop
  1366. + reactionCenter
  1367. - visibleAreaHeight / 2;
  1368. // Show the reaction center and some space above it for the effect.
  1369. const auto maxAllowed = itemTop + reactionCenter - 2 * maxSize;
  1370. return std::max(
  1371. std::min(maxAllowed, std::max(bottomResult, byReactionResult)),
  1372. 0);
  1373. }
  1374. return itemTop;
  1375. }
  1376. void HistoryWidget::initFieldAutocomplete() {
  1377. _emojiSuggestions = nullptr;
  1378. _autocomplete = nullptr;
  1379. if (!_peer) {
  1380. return;
  1381. }
  1382. const auto processShortcut = [=](QString shortcut) {
  1383. if (!_peer) {
  1384. return;
  1385. }
  1386. const auto messages = &_peer->owner().shortcutMessages();
  1387. const auto shortcutId = messages->lookupShortcutId(shortcut);
  1388. if (shortcut.isEmpty()) {
  1389. controller()->showSettings(Settings::QuickRepliesId());
  1390. } else if (!_peer->session().premium()) {
  1391. ShowPremiumPreviewToBuy(
  1392. controller(),
  1393. PremiumFeature::QuickReplies);
  1394. } else if (shortcutId) {
  1395. session().api().sendShortcutMessages(_peer, shortcutId);
  1396. session().api().finishForwarding(prepareSendAction({}));
  1397. setFieldText(_field->getTextWithTagsPart(
  1398. _field->textCursor().position()));
  1399. }
  1400. };
  1401. ChatHelpers::InitFieldAutocomplete(_autocomplete, {
  1402. .parent = this,
  1403. .show = controller()->uiShow(),
  1404. .field = _field.data(),
  1405. .peer = _peer,
  1406. .features = [=] {
  1407. auto result = ChatHelpers::ComposeFeatures();
  1408. if (_showAnimation
  1409. || isChoosingTheme()
  1410. || (_inlineBot && !_inlineLookingUpBot)) {
  1411. result.autocompleteMentions = false;
  1412. result.autocompleteHashtags = false;
  1413. result.autocompleteCommands = false;
  1414. }
  1415. if (_editMsgId) {
  1416. result.autocompleteCommands = false;
  1417. result.suggestStickersByEmoji = false;
  1418. }
  1419. return result;
  1420. },
  1421. .sendMenuDetails = [=] { return sendMenuDetails(); },
  1422. .stickerChoosing = [=] {
  1423. if (_history) {
  1424. session().sendProgressManager().update(
  1425. _history,
  1426. Api::SendProgressType::ChooseSticker);
  1427. }
  1428. },
  1429. .stickerChosen = [=](ChatHelpers::FileChosen &&data) {
  1430. fileChosen(std::move(data));
  1431. },
  1432. .setText = [=](TextWithTags text) { if (_peer) setFieldText(text); },
  1433. .sendBotCommand = [=](QString command) {
  1434. if (_peer) {
  1435. sendBotCommand({ _peer, command, FullMsgId(), replyTo() });
  1436. session().api().finishForwarding(prepareSendAction({}));
  1437. }
  1438. },
  1439. .processShortcut = processShortcut,
  1440. .moderateKeyActivateCallback = [=](int key) {
  1441. const auto context = [=](FullMsgId itemId) {
  1442. return _list->prepareClickContext(Qt::LeftButton, itemId);
  1443. };
  1444. return !_keyboard->isHidden() && _keyboard->moderateKeyActivate(
  1445. key,
  1446. context);
  1447. },
  1448. });
  1449. const auto allow = [=](const auto&) {
  1450. return _peer->isSelf();
  1451. };
  1452. _emojiSuggestions.reset(Ui::Emoji::SuggestionsController::Init(
  1453. this,
  1454. _field,
  1455. &controller()->session(),
  1456. { .suggestCustomEmoji = true, .allowCustomWithoutPremium = allow }));
  1457. }
  1458. InlineBotQuery HistoryWidget::parseInlineBotQuery() const {
  1459. return (isChoosingTheme() || _editMsgId)
  1460. ? InlineBotQuery()
  1461. : ParseInlineBotQuery(&session(), _field);
  1462. }
  1463. void HistoryWidget::updateInlineBotQuery() {
  1464. if (!_history) {
  1465. return;
  1466. }
  1467. const auto query = parseInlineBotQuery();
  1468. if (_inlineBotUsername != query.username) {
  1469. _inlineBotUsername = query.username;
  1470. if (_inlineBotResolveRequestId) {
  1471. _api.request(_inlineBotResolveRequestId).cancel();
  1472. _inlineBotResolveRequestId = 0;
  1473. }
  1474. if (query.lookingUpBot) {
  1475. _inlineBot = nullptr;
  1476. _inlineLookingUpBot = true;
  1477. const auto username = _inlineBotUsername;
  1478. _inlineBotResolveRequestId = _api.request(
  1479. MTPcontacts_ResolveUsername(
  1480. MTP_flags(0),
  1481. MTP_string(username),
  1482. MTP_string())
  1483. ).done([=](const MTPcontacts_ResolvedPeer &result) {
  1484. const auto &data = result.data();
  1485. const auto resolvedBot = [&]() -> UserData* {
  1486. if (const auto user = session().data().processUsers(
  1487. data.vusers())) {
  1488. if (user->isBot()
  1489. && !user->botInfo->inlinePlaceholder.isEmpty()) {
  1490. return user;
  1491. }
  1492. }
  1493. return nullptr;
  1494. }();
  1495. session().data().processChats(data.vchats());
  1496. _inlineBotResolveRequestId = 0;
  1497. const auto query = parseInlineBotQuery();
  1498. if (_inlineBotUsername == query.username) {
  1499. applyInlineBotQuery(
  1500. query.lookingUpBot ? resolvedBot : query.bot,
  1501. query.query);
  1502. } else {
  1503. clearInlineBot();
  1504. }
  1505. }).fail([=](const MTP::Error &error) {
  1506. _inlineBotResolveRequestId = 0;
  1507. if (username == _inlineBotUsername) {
  1508. clearInlineBot();
  1509. }
  1510. }).send();
  1511. } else {
  1512. applyInlineBotQuery(query.bot, query.query);
  1513. }
  1514. } else if (query.lookingUpBot) {
  1515. if (!_inlineLookingUpBot) {
  1516. applyInlineBotQuery(_inlineBot, query.query);
  1517. }
  1518. } else {
  1519. applyInlineBotQuery(query.bot, query.query);
  1520. }
  1521. }
  1522. void HistoryWidget::applyInlineBotQuery(UserData *bot, const QString &query) {
  1523. if (bot) {
  1524. if (_inlineBot != bot) {
  1525. _inlineBot = bot;
  1526. _inlineLookingUpBot = false;
  1527. inlineBotChanged();
  1528. }
  1529. if (!_inlineResults) {
  1530. _inlineResults.create(this, controller());
  1531. _inlineResults->setResultSelectedCallback([=](
  1532. InlineBots::ResultSelected result) {
  1533. if (result.open) {
  1534. const auto request = result.result->openRequest();
  1535. if (const auto photo = request.photo()) {
  1536. controller()->openPhoto(photo, {});
  1537. } else if (const auto document = request.document()) {
  1538. controller()->openDocument(document, false, {});
  1539. }
  1540. } else {
  1541. sendInlineResult(result);
  1542. }
  1543. });
  1544. _inlineResults->setSendMenuDetails([=] {
  1545. return sendMenuDetails();
  1546. });
  1547. _inlineResults->requesting(
  1548. ) | rpl::start_with_next([=](bool requesting) {
  1549. _tabbedSelectorToggle->setLoading(requesting);
  1550. }, _inlineResults->lifetime());
  1551. updateControlsGeometry();
  1552. orderWidgets();
  1553. }
  1554. _inlineResults->queryInlineBot(_inlineBot, _peer, query);
  1555. if (_autocomplete) {
  1556. _autocomplete->hideAnimated();
  1557. }
  1558. } else {
  1559. clearInlineBot();
  1560. }
  1561. }
  1562. void HistoryWidget::orderWidgets() {
  1563. _voiceRecordBar->raise();
  1564. _send->raise();
  1565. if (_businessBotStatus) {
  1566. _businessBotStatus->bar().raise();
  1567. }
  1568. if (_contactStatus) {
  1569. _contactStatus->bar().raise();
  1570. }
  1571. if (_paysStatus) {
  1572. _paysStatus->bar().raise();
  1573. }
  1574. if (_translateBar) {
  1575. _translateBar->raise();
  1576. }
  1577. if (_sponsoredMessageBar) {
  1578. _sponsoredMessageBar->raise();
  1579. }
  1580. if (_pinnedBar) {
  1581. _pinnedBar->raise();
  1582. }
  1583. if (_requestsBar) {
  1584. _requestsBar->raise();
  1585. }
  1586. if (_groupCallBar) {
  1587. _groupCallBar->raise();
  1588. }
  1589. if (_chooseTheme) {
  1590. _chooseTheme->raise();
  1591. }
  1592. _topShadow->raise();
  1593. if (_autocomplete) {
  1594. _autocomplete->raise();
  1595. }
  1596. if (_membersDropdown) {
  1597. _membersDropdown->raise();
  1598. }
  1599. if (_inlineResults) {
  1600. _inlineResults->raise();
  1601. }
  1602. if (_tabbedPanel) {
  1603. _tabbedPanel->raise();
  1604. }
  1605. if (_emojiSuggestions) {
  1606. _emojiSuggestions->raise();
  1607. }
  1608. _attachDragAreas.document->raise();
  1609. _attachDragAreas.photo->raise();
  1610. }
  1611. void HistoryWidget::toggleChooseChatTheme(
  1612. not_null<PeerData*> peer,
  1613. std::optional<bool> show) {
  1614. const auto update = [=] {
  1615. updateInlineBotQuery();
  1616. updateControlsGeometry();
  1617. updateControlsVisibility();
  1618. };
  1619. if (peer.get() != _peer) {
  1620. return;
  1621. } else if (_chooseTheme) {
  1622. if (isChoosingTheme() && !show.value_or(false)) {
  1623. const auto was = base::take(_chooseTheme);
  1624. if (Ui::InFocusChain(this)) {
  1625. setInnerFocus();
  1626. }
  1627. update();
  1628. }
  1629. return;
  1630. } else if (!show.value_or(true)) {
  1631. return;
  1632. } else if (_voiceRecordBar->isActive()) {
  1633. controller()->showToast(tr::lng_chat_theme_cant_voice(tr::now));
  1634. return;
  1635. }
  1636. _chooseTheme = std::make_unique<Ui::ChooseThemeController>(
  1637. this,
  1638. controller(),
  1639. peer);
  1640. _chooseTheme->shouldBeShownValue(
  1641. ) | rpl::start_with_next(update, _chooseTheme->lifetime());
  1642. }
  1643. Ui::ChatTheme *HistoryWidget::customChatTheme() const {
  1644. return _list ? _list->theme().get() : nullptr;
  1645. }
  1646. void HistoryWidget::fieldChanged() {
  1647. const auto updateTyping = (_textUpdateEvents
  1648. & TextUpdateEvent::SendTyping);
  1649. InvokeQueued(this, [=] {
  1650. updateInlineBotQuery();
  1651. if (_history
  1652. && !_inlineBot
  1653. && !_editMsgId
  1654. && (!_autocomplete || !_autocomplete->stickersEmoji())
  1655. && updateTyping) {
  1656. session().sendProgressManager().update(
  1657. _history,
  1658. Api::SendProgressType::Typing);
  1659. }
  1660. });
  1661. checkCharsCount();
  1662. updateSendButtonType();
  1663. if (!HasSendText(_field)) {
  1664. _fieldIsEmpty = true;
  1665. } else if (_fieldIsEmpty) {
  1666. _fieldIsEmpty = false;
  1667. if (_kbShown) {
  1668. toggleKeyboard();
  1669. }
  1670. }
  1671. if (updateCmdStartShown()) {
  1672. updateControlsVisibility();
  1673. updateControlsGeometry();
  1674. }
  1675. _saveCloudDraftTimer.cancel();
  1676. if (!_peer || !(_textUpdateEvents & TextUpdateEvent::SaveDraft)) {
  1677. return;
  1678. }
  1679. _saveDraftText = true;
  1680. saveDraft(true);
  1681. }
  1682. void HistoryWidget::saveDraftDelayed() {
  1683. if (!_peer || !(_textUpdateEvents & TextUpdateEvent::SaveDraft)) {
  1684. return;
  1685. }
  1686. if (!_field->textCursor().position()
  1687. && !_field->textCursor().anchor()
  1688. && !_field->scrollTop().current()) {
  1689. if (!session().local().hasDraftCursors(_peer->id)) {
  1690. return;
  1691. }
  1692. }
  1693. saveDraft(true);
  1694. }
  1695. void HistoryWidget::saveDraft(bool delayed) {
  1696. if (!_peer) {
  1697. return;
  1698. } else if (delayed) {
  1699. auto ms = crl::now();
  1700. if (!_saveDraftStart) {
  1701. _saveDraftStart = ms;
  1702. return _saveDraftTimer.callOnce(kSaveDraftTimeout);
  1703. } else if (ms - _saveDraftStart < kSaveDraftAnywayTimeout) {
  1704. return _saveDraftTimer.callOnce(kSaveDraftTimeout);
  1705. }
  1706. }
  1707. writeDrafts();
  1708. }
  1709. void HistoryWidget::saveFieldToHistoryLocalDraft() {
  1710. if (!_history) {
  1711. return;
  1712. }
  1713. const auto topicRootId = MsgId();
  1714. if (_editMsgId) {
  1715. _history->setLocalEditDraft(std::make_unique<Data::Draft>(
  1716. _field,
  1717. FullReplyTo{
  1718. .messageId = FullMsgId(_history->peer->id, _editMsgId),
  1719. .topicRootId = topicRootId,
  1720. },
  1721. _preview->draft(),
  1722. _saveEditMsgRequestId));
  1723. } else {
  1724. if (_replyTo || !_field->empty()) {
  1725. _history->setLocalDraft(std::make_unique<Data::Draft>(
  1726. _field,
  1727. _replyTo,
  1728. _preview->draft()));
  1729. } else {
  1730. _history->clearLocalDraft(topicRootId);
  1731. }
  1732. _history->clearLocalEditDraft(topicRootId);
  1733. }
  1734. }
  1735. void HistoryWidget::fileChosen(ChatHelpers::FileChosen &&data) {
  1736. controller()->hideLayer(anim::type::normal);
  1737. if (const auto info = data.document->sticker()
  1738. ; info && info->setType == Data::StickersType::Emoji) {
  1739. if (data.document->isPremiumEmoji()
  1740. && !session().premium()
  1741. && (!_peer
  1742. || !Data::AllowEmojiWithoutPremium(
  1743. _peer,
  1744. data.document))) {
  1745. showPremiumToast(data.document);
  1746. } else if (!_field->isHidden()) {
  1747. Data::InsertCustomEmoji(_field.data(), data.document);
  1748. }
  1749. } else if (_history) {
  1750. controller()->sendingAnimation().appendSending(
  1751. data.messageSendingFrom);
  1752. const auto localId = data.messageSendingFrom.localId;
  1753. auto messageToSend = Api::MessageToSend(
  1754. prepareSendAction(data.options));
  1755. messageToSend.textWithTags = base::take(data.caption);
  1756. sendExistingDocument(
  1757. data.document,
  1758. std::move(messageToSend),
  1759. localId);
  1760. }
  1761. }
  1762. void HistoryWidget::saveCloudDraft() {
  1763. controller()->session().api().saveCurrentDraftToCloud();
  1764. }
  1765. void HistoryWidget::writeDraftTexts() {
  1766. Expects(_history != nullptr);
  1767. session().local().writeDrafts(_history);
  1768. if (_migrated) {
  1769. _migrated->clearDrafts();
  1770. session().local().writeDrafts(_migrated);
  1771. }
  1772. }
  1773. void HistoryWidget::writeDraftCursors() {
  1774. Expects(_history != nullptr);
  1775. session().local().writeDraftCursors(_history);
  1776. if (_migrated) {
  1777. _migrated->clearDrafts();
  1778. session().local().writeDraftCursors(_migrated);
  1779. }
  1780. }
  1781. void HistoryWidget::writeDrafts() {
  1782. const auto save = (_history != nullptr) && (_saveDraftStart > 0);
  1783. _saveDraftStart = 0;
  1784. _saveDraftTimer.cancel();
  1785. if (save) {
  1786. if (_saveDraftText) {
  1787. writeDraftTexts();
  1788. }
  1789. writeDraftCursors();
  1790. }
  1791. _saveDraftText = false;
  1792. if (!_editMsgId && !_inlineBot) {
  1793. _saveCloudDraftTimer.callOnce(kSaveCloudDraftIdleTimeout);
  1794. }
  1795. }
  1796. bool HistoryWidget::isRecording() const {
  1797. return _voiceRecordBar->isRecording();
  1798. }
  1799. void HistoryWidget::activate() {
  1800. if (_history) {
  1801. if (!_historyInited) {
  1802. updateHistoryGeometry(true);
  1803. } else if (hasPendingResizedItems()) {
  1804. updateHistoryGeometry();
  1805. }
  1806. }
  1807. controller()->widget()->setInnerFocus();
  1808. }
  1809. void HistoryWidget::setInnerFocus() {
  1810. if (_list) {
  1811. if (isSearching() && !_nonEmptySelection) {
  1812. _composeSearch->setInnerFocus();
  1813. } else if (isChoosingTheme()) {
  1814. _chooseTheme->setFocus();
  1815. } else if (_showAnimation
  1816. || _nonEmptySelection
  1817. || (_list && _list->wasSelectedText())
  1818. || isRecording()
  1819. || isJoinChannel()
  1820. || isBotStart()
  1821. || isBlocked()
  1822. || (!_canSendTexts && !_editMsgId)) {
  1823. if (_scroll->isHidden()) {
  1824. setFocus();
  1825. } else {
  1826. _list->setFocus();
  1827. }
  1828. } else {
  1829. _field->setFocus();
  1830. }
  1831. } else if (_scroll->isHidden()) {
  1832. setFocus();
  1833. }
  1834. }
  1835. bool HistoryWidget::notify_switchInlineBotButtonReceived(
  1836. const QString &query,
  1837. UserData *samePeerBot,
  1838. MsgId samePeerReplyTo) {
  1839. if (samePeerBot) {
  1840. const auto to = controller()->dialogsEntryStateCurrent();
  1841. if (!to.key.owningHistory()) {
  1842. return false;
  1843. }
  1844. controller()->switchInlineQuery(to, samePeerBot, query);
  1845. return true;
  1846. } else if (const auto bot = _peer ? _peer->asUser() : nullptr) {
  1847. const auto to = bot->isBot()
  1848. ? bot->botInfo->inlineReturnTo
  1849. : Dialogs::EntryState();
  1850. if (!to.key.owningHistory()) {
  1851. return false;
  1852. }
  1853. bot->botInfo->inlineReturnTo = Dialogs::EntryState();
  1854. controller()->switchInlineQuery(to, bot, query);
  1855. return true;
  1856. }
  1857. return false;
  1858. }
  1859. void HistoryWidget::tryProcessKeyInput(not_null<QKeyEvent*> e) {
  1860. e->accept();
  1861. keyPressEvent(e);
  1862. if (!e->isAccepted()
  1863. && _canSendTexts
  1864. && _field->isVisible()
  1865. && !e->text().isEmpty()) {
  1866. _field->setFocusFast();
  1867. QCoreApplication::sendEvent(_field->rawTextEdit(), e);
  1868. }
  1869. }
  1870. void HistoryWidget::setupShortcuts() {
  1871. Shortcuts::Requests(
  1872. ) | rpl::filter([=] {
  1873. return _history
  1874. && Ui::AppInFocus()
  1875. && Ui::InFocusChain(this)
  1876. && !controller()->isLayerShown()
  1877. && window()->isActiveWindow();
  1878. }) | rpl::start_with_next([=](not_null<Shortcuts::Request*> request) {
  1879. using Command = Shortcuts::Command;
  1880. request->check(Command::Search, 1) && request->handle([=] {
  1881. controller()->searchInChat(_history);
  1882. return true;
  1883. });
  1884. request->check(Command::ShowChatMenu, 1) && request->handle([=] {
  1885. Window::ActivateWindow(controller());
  1886. _topBar->showPeerMenu();
  1887. return true;
  1888. });
  1889. _canSendMessages
  1890. && request->check(Command::ShowScheduled, 1)
  1891. && request->handle([=] {
  1892. using Scheduled = HistoryView::ScheduledMemento;
  1893. controller()->showSection(
  1894. std::make_shared<Scheduled>(_history));
  1895. return true;
  1896. });
  1897. if (session().supportMode()) {
  1898. request->check(
  1899. Command::SupportToggleMuted
  1900. ) && request->handle([=] {
  1901. toggleMuteUnmute();
  1902. return true;
  1903. });
  1904. }
  1905. }, lifetime());
  1906. }
  1907. void HistoryWidget::setupGiftToChannelButton() {
  1908. const auto setupButton = [=](not_null<Ui::RpWidget*> parent) {
  1909. auto *button = Ui::CreateChild<Ui::IconButton>(
  1910. parent.get(),
  1911. st::historyGiftToChannel);
  1912. parent->widthValue() | rpl::start_with_next([=](int width) {
  1913. button->moveToRight(0, 0);
  1914. }, button->lifetime());
  1915. button->setClickedCallback([=] {
  1916. if (_peer) {
  1917. Ui::ShowStarGiftBox(controller(), _peer);
  1918. }
  1919. });
  1920. return button;
  1921. };
  1922. _giftToChannelIn = setupButton(_muteUnmute);
  1923. _giftToChannelOut = setupButton(_joinChannel);
  1924. }
  1925. void HistoryWidget::pushReplyReturn(not_null<HistoryItem*> item) {
  1926. if (item->history() != _history && item->history() != _migrated) {
  1927. return;
  1928. }
  1929. _cornerButtons.pushReplyReturn(item);
  1930. updateControlsVisibility();
  1931. }
  1932. QVector<FullMsgId> HistoryWidget::replyReturns() const {
  1933. return _cornerButtons.replyReturns();
  1934. }
  1935. void HistoryWidget::setReplyReturns(
  1936. PeerId peer,
  1937. QVector<FullMsgId> replyReturns) {
  1938. if (!_peer || _peer->id != peer) {
  1939. return;
  1940. }
  1941. _cornerButtons.setReplyReturns(std::move(replyReturns));
  1942. }
  1943. void HistoryWidget::fastShowAtEnd(not_null<History*> history) {
  1944. if (_history != history) {
  1945. return;
  1946. }
  1947. clearAllLoadRequests();
  1948. setMsgId(ShowAtUnreadMsgId);
  1949. _pinnedClickedId = FullMsgId();
  1950. _minPinnedId = std::nullopt;
  1951. if (_history->isReadyFor(_showAtMsgId)) {
  1952. historyLoaded();
  1953. } else {
  1954. firstLoadMessages();
  1955. doneShow();
  1956. }
  1957. }
  1958. bool HistoryWidget::applyDraft(FieldHistoryAction fieldHistoryAction) {
  1959. InvokeQueued(this, [=] {
  1960. if (_autocomplete) {
  1961. _autocomplete->requestStickersUpdate();
  1962. }
  1963. });
  1964. const auto editDraft = _history ? _history->localEditDraft({}) : nullptr;
  1965. const auto draft = editDraft
  1966. ? editDraft
  1967. : _history
  1968. ? _history->localDraft({})
  1969. : nullptr;
  1970. auto fieldAvailable = canWriteMessage();
  1971. const auto editMsgId = editDraft ? editDraft->reply.messageId.msg : 0;
  1972. if (_voiceRecordBar->isActive() || (!_canSendTexts && !editMsgId)) {
  1973. if (!_canSendTexts) {
  1974. clearFieldText(0, fieldHistoryAction);
  1975. }
  1976. return false;
  1977. }
  1978. if (!draft || (!editDraft && !fieldAvailable)) {
  1979. const auto fieldWillBeHiddenAfterEdit = (!fieldAvailable
  1980. && _editMsgId != 0);
  1981. clearFieldText(0, fieldHistoryAction);
  1982. setInnerFocus();
  1983. _processingReplyItem = _replyEditMsg = nullptr;
  1984. _processingReplyTo = _replyTo = FullReplyTo();
  1985. setEditMsgId(0);
  1986. if (_preview) {
  1987. _preview->apply({ .removed = true });
  1988. }
  1989. if (fieldWillBeHiddenAfterEdit) {
  1990. updateControlsVisibility();
  1991. updateControlsGeometry();
  1992. }
  1993. refreshTopBarActiveChat();
  1994. return true;
  1995. }
  1996. _textUpdateEvents = 0;
  1997. setFieldText(draft->textWithTags, 0, fieldHistoryAction);
  1998. setInnerFocus();
  1999. draft->cursor.applyTo(_field);
  2000. _textUpdateEvents = TextUpdateEvent::SaveDraft
  2001. | TextUpdateEvent::SendTyping;
  2002. _processingReplyItem = _replyEditMsg = nullptr;
  2003. _processingReplyTo = _replyTo = FullReplyTo();
  2004. setEditMsgId(editMsgId);
  2005. updateCmdStartShown();
  2006. updateControlsVisibility();
  2007. updateControlsGeometry();
  2008. refreshTopBarActiveChat();
  2009. if (_editMsgId) {
  2010. updateReplyEditTexts();
  2011. if (!_replyEditMsg) {
  2012. requestMessageData(_editMsgId);
  2013. }
  2014. } else {
  2015. const auto draft = _history->localDraft({});
  2016. _processingReplyTo = draft ? draft->reply : FullReplyTo();
  2017. if (_processingReplyTo) {
  2018. _processingReplyItem = session().data().message(
  2019. _processingReplyTo.messageId);
  2020. }
  2021. processReply();
  2022. }
  2023. if (_preview) {
  2024. _preview->setDisabled(_editMsgId
  2025. && _replyEditMsg
  2026. && _replyEditMsg->media()
  2027. && !_replyEditMsg->media()->webpage());
  2028. if (!_editMsgId) {
  2029. _preview->apply(draft->webpage, true);
  2030. } else if (!_replyEditMsg
  2031. || !_replyEditMsg->media()
  2032. || _replyEditMsg->media()->webpage()) {
  2033. _preview->apply(draft->webpage, false);
  2034. }
  2035. }
  2036. return true;
  2037. }
  2038. void HistoryWidget::applyCloudDraft(History *history) {
  2039. Expects(!session().supportMode());
  2040. if (_history == history && !_editMsgId) {
  2041. applyDraft(Ui::InputField::HistoryAction::NewEntry);
  2042. updateControlsVisibility();
  2043. updateControlsGeometry();
  2044. }
  2045. }
  2046. bool HistoryWidget::insideJumpToEndInsteadOfToUnread() const {
  2047. Expects(_history != nullptr);
  2048. if (session().supportMode() || !_history->trackUnreadMessages()) {
  2049. return true;
  2050. } else if (!_historyInited) {
  2051. return false;
  2052. }
  2053. _history->calculateFirstUnreadMessage();
  2054. const auto unread = _history->firstUnreadMessage();
  2055. const auto visibleBottom = _scroll->scrollTop() + _scroll->height();
  2056. DEBUG_LOG(("JumpToEnd(%1, %2, %3): "
  2057. "unread: %4, top: %5, visibleBottom: %6."
  2058. ).arg(_history->peer->name()
  2059. ).arg(_history->inboxReadTillId().bare
  2060. ).arg(Logs::b(_history->loadedAtBottom())
  2061. ).arg(unread ? unread->data()->id.bare : 0
  2062. ).arg(unread ? _list->itemTop(unread) : -1
  2063. ).arg(visibleBottom));
  2064. return unread && _list->itemTop(unread) <= visibleBottom;
  2065. }
  2066. void HistoryWidget::showHistory(
  2067. PeerId peerId,
  2068. MsgId showAtMsgId,
  2069. const Window::SectionShow &params) {
  2070. _pinnedClickedId = FullMsgId();
  2071. _minPinnedId = std::nullopt;
  2072. _showAtMsgParams = {};
  2073. const auto wasState = controller()->dialogsEntryStateCurrent();
  2074. const auto startBot = (showAtMsgId == ShowAndStartBotMsgId);
  2075. _showAndMaybeSendStart = (showAtMsgId == ShowAndMaybeStartBotMsgId);
  2076. if (startBot || _showAndMaybeSendStart) {
  2077. showAtMsgId = ShowAtTheEndMsgId;
  2078. }
  2079. _highlighter.clear();
  2080. controller()->sendingAnimation().clear();
  2081. _topToast.hide(anim::type::instant);
  2082. if (_history) {
  2083. if (_peer->id == peerId) {
  2084. updateForwarding();
  2085. if (showAtMsgId == ShowAtUnreadMsgId
  2086. && insideJumpToEndInsteadOfToUnread()) {
  2087. DEBUG_LOG(("JumpToEnd(%1, %2, %3): "
  2088. "Jumping to end instead of unread."
  2089. ).arg(_history->peer->name()
  2090. ).arg(_history->inboxReadTillId().bare
  2091. ).arg(Logs::b(_history->loadedAtBottom())));
  2092. showAtMsgId = ShowAtTheEndMsgId;
  2093. } else if (showAtMsgId == ShowForChooseMessagesMsgId) {
  2094. if (_chooseForReport) {
  2095. clearSelected();
  2096. _chooseForReport->active = true;
  2097. _list->setChooseReportReason(
  2098. _chooseForReport->reportInput);
  2099. updateControlsVisibility();
  2100. updateControlsGeometry();
  2101. updateTopBarChooseForReport();
  2102. }
  2103. return;
  2104. }
  2105. if (!IsServerMsgId(showAtMsgId)
  2106. && !IsClientMsgId(showAtMsgId)
  2107. && !IsServerMsgId(-showAtMsgId)) {
  2108. // To end or to unread.
  2109. destroyUnreadBar();
  2110. }
  2111. const auto canShowNow = _history->isReadyFor(showAtMsgId);
  2112. if (!canShowNow) {
  2113. if (!_firstLoadRequest) {
  2114. DEBUG_LOG(("JumpToEnd(%1, %2, %3): Showing delayed at %4."
  2115. ).arg(_history->peer->name()
  2116. ).arg(_history->inboxReadTillId().bare
  2117. ).arg(Logs::b(_history->loadedAtBottom())
  2118. ).arg(showAtMsgId.bare));
  2119. delayedShowAt(showAtMsgId, params);
  2120. } else if (_showAtMsgId != showAtMsgId) {
  2121. clearAllLoadRequests();
  2122. setMsgId(showAtMsgId, params);
  2123. firstLoadMessages();
  2124. doneShow();
  2125. }
  2126. } else {
  2127. _history->forgetScrollState();
  2128. if (_migrated) {
  2129. _migrated->forgetScrollState();
  2130. }
  2131. clearDelayedShowAt();
  2132. const auto skipId = (_migrated && showAtMsgId < 0)
  2133. ? FullMsgId(_migrated->peer->id, -showAtMsgId)
  2134. : (showAtMsgId > 0)
  2135. ? FullMsgId(_history->peer->id, showAtMsgId)
  2136. : FullMsgId();
  2137. if (skipId) {
  2138. _cornerButtons.skipReplyReturn(skipId);
  2139. }
  2140. setMsgId(showAtMsgId, params);
  2141. if (_historyInited) {
  2142. DEBUG_LOG(("JumpToEnd(%1, %2, %3): "
  2143. "Showing instant at %4."
  2144. ).arg(_history->peer->name()
  2145. ).arg(_history->inboxReadTillId().bare
  2146. ).arg(Logs::b(_history->loadedAtBottom())
  2147. ).arg(showAtMsgId.bare));
  2148. const auto to = countInitialScrollTop();
  2149. const auto item = getItemFromHistoryOrMigrated(
  2150. _showAtMsgId);
  2151. animatedScrollToY(
  2152. std::clamp(to, 0, _scroll->scrollTopMax()),
  2153. item);
  2154. } else {
  2155. historyLoaded();
  2156. }
  2157. }
  2158. _topBar->update();
  2159. update();
  2160. if (const auto user = _peer->asUser()) {
  2161. if (const auto &info = user->botInfo) {
  2162. if (startBot || clearMaybeSendStart()) {
  2163. if (startBot && wasState.key) {
  2164. info->inlineReturnTo = wasState;
  2165. }
  2166. sendBotStartCommand();
  2167. _history->clearLocalDraft({});
  2168. applyDraft();
  2169. _send->finishAnimating();
  2170. }
  2171. }
  2172. }
  2173. return;
  2174. } else {
  2175. _sponsoredMessagesStateKnown = false;
  2176. session().sponsoredMessages().clearItems(_history);
  2177. session().data().hideShownSpoilers();
  2178. _composeSearch = nullptr;
  2179. }
  2180. session().sendProgressManager().update(
  2181. _history,
  2182. Api::SendProgressType::Typing,
  2183. -1);
  2184. session().data().histories().sendPendingReadInbox(_history);
  2185. session().sendProgressManager().cancelTyping(_history);
  2186. }
  2187. _cornerButtons.clearReplyReturns();
  2188. if (_history) {
  2189. if (Ui::InFocusChain(this)) {
  2190. // Removing focus from list clears selected and updates top bar.
  2191. setFocus();
  2192. }
  2193. controller()->session().api().saveCurrentDraftToCloud();
  2194. if (_migrated) {
  2195. _migrated->clearDrafts(); // use migrated draft only once
  2196. }
  2197. _history->showAtMsgId = _showAtMsgId;
  2198. destroyUnreadBarOnClose();
  2199. _sponsoredMessageBar = nullptr;
  2200. _pinnedBar = nullptr;
  2201. _translateBar = nullptr;
  2202. _pinnedTracker = nullptr;
  2203. _groupCallBar = nullptr;
  2204. _requestsBar = nullptr;
  2205. _chooseTheme = nullptr;
  2206. _membersDropdown.destroy();
  2207. _scrollToAnimation.stop();
  2208. setHistory(nullptr);
  2209. _list = nullptr;
  2210. _peer = nullptr;
  2211. _sendPayment.clear();
  2212. _topicsRequested.clear();
  2213. _canSendMessages = false;
  2214. _canSendTexts = false;
  2215. _fieldDisabled = nullptr;
  2216. _silent.destroy();
  2217. updateBotKeyboard();
  2218. } else {
  2219. Assert(_list == nullptr);
  2220. }
  2221. HistoryView::Element::ClearGlobal();
  2222. _saveEditMsgRequestId = 0;
  2223. _processingReplyItem = _replyEditMsg = nullptr;
  2224. _processingReplyTo = _replyTo = FullReplyTo();
  2225. _editMsgId = MsgId();
  2226. _canReplaceMedia = _canAddMedia = false;
  2227. _photoEditMedia = nullptr;
  2228. updateReplaceMediaButton();
  2229. _fieldBarCancel->hide();
  2230. _membersDropdownShowTimer.cancel();
  2231. _scroll->takeWidget<HistoryInner>().destroy();
  2232. clearInlineBot();
  2233. _showAtMsgId = showAtMsgId;
  2234. _showAtMsgParams = params;
  2235. _historyInited = false;
  2236. _paysStatus = nullptr;
  2237. _contactStatus = nullptr;
  2238. _businessBotStatus = nullptr;
  2239. Core::App().mediaDevices().refreshRecordAvailability();
  2240. if (peerId) {
  2241. using namespace HistoryView;
  2242. _peer = session().data().peer(peerId);
  2243. _contactStatus = std::make_unique<ContactStatus>(
  2244. controller(),
  2245. this,
  2246. _peer,
  2247. false);
  2248. _contactStatus->bar().heightValue(
  2249. ) | rpl::start_with_next([=] {
  2250. updateControlsGeometry();
  2251. }, _contactStatus->bar().lifetime());
  2252. refreshGiftToChannelShown();
  2253. if (const auto user = _peer->asUser()) {
  2254. _paysStatus = std::make_unique<PaysStatus>(
  2255. controller(),
  2256. this,
  2257. user);
  2258. _paysStatus->bar().heightValue(
  2259. ) | rpl::start_with_next([=] {
  2260. updateControlsGeometry();
  2261. }, _paysStatus->bar().lifetime());
  2262. _businessBotStatus = std::make_unique<BusinessBotStatus>(
  2263. controller(),
  2264. this,
  2265. user);
  2266. _businessBotStatus->bar().heightValue(
  2267. ) | rpl::start_with_next([=] {
  2268. updateControlsGeometry();
  2269. }, _businessBotStatus->bar().lifetime());
  2270. }
  2271. orderWidgets();
  2272. controller()->tabbedSelector()->setCurrentPeer(_peer);
  2273. }
  2274. refreshTabbedPanel();
  2275. initFieldAutocomplete();
  2276. if (_peer) {
  2277. _unblock->setText(((_peer->isUser()
  2278. && _peer->asUser()->isBot()
  2279. && !_peer->asUser()->isSupport())
  2280. ? tr::lng_restart_button(tr::now)
  2281. : tr::lng_unblock_button(tr::now)).toUpper());
  2282. }
  2283. _nonEmptySelection = false;
  2284. _itemRevealPending.clear();
  2285. _itemRevealAnimations.clear();
  2286. _itemsRevealHeight = 0;
  2287. if (_peer) {
  2288. setHistory(_peer->owner().history(_peer));
  2289. if (_migrated
  2290. && !_migrated->isEmpty()
  2291. && (!_history->loadedAtTop() || !_migrated->loadedAtBottom())) {
  2292. _migrated->clear(History::ClearType::Unload);
  2293. }
  2294. _history->setFakeUnreadWhileOpened(true);
  2295. if (_showAtMsgId == ShowForChooseMessagesMsgId) {
  2296. _showAtMsgId = ShowAtUnreadMsgId;
  2297. if (_chooseForReport) {
  2298. _chooseForReport->active = true;
  2299. }
  2300. } else {
  2301. _chooseForReport = nullptr;
  2302. }
  2303. if (_showAtMsgId == ShowAtUnreadMsgId
  2304. && !_history->trackUnreadMessages()
  2305. && !hasSavedScroll()) {
  2306. _showAtMsgId = ShowAtTheEndMsgId;
  2307. }
  2308. refreshTopBarActiveChat();
  2309. updateTopBarSelection();
  2310. if (_peer->isChannel()) {
  2311. updateNotifyControls();
  2312. session().data().notifySettings().request(_peer);
  2313. refreshSilentToggle();
  2314. } else if (_peer->isRepliesChat() || _peer->isVerifyCodes()) {
  2315. updateNotifyControls();
  2316. }
  2317. refreshScheduledToggle();
  2318. refreshSendAsToggle();
  2319. if (_showAtMsgId == ShowAtUnreadMsgId) {
  2320. if (_history->scrollTopItem) {
  2321. _showAtMsgId = _history->showAtMsgId;
  2322. }
  2323. } else {
  2324. _history->forgetScrollState();
  2325. if (_migrated) {
  2326. _migrated->forgetScrollState();
  2327. }
  2328. }
  2329. _scroll->hide();
  2330. _list = _scroll->setOwnedWidget(
  2331. object_ptr<HistoryInner>(this, _scroll, controller(), _history));
  2332. _list->sendIntroSticker(
  2333. ) | rpl::start_with_next([=](not_null<DocumentData*> sticker) {
  2334. sendExistingDocument(
  2335. sticker,
  2336. Api::MessageToSend(prepareSendAction({})));
  2337. }, _list->lifetime());
  2338. _list->show();
  2339. if (const auto channel = _peer->asChannel()) {
  2340. channel->updateFull();
  2341. if (!channel->isBroadcast()) {
  2342. channel->flagsValue(
  2343. ) | rpl::start_with_next([=] {
  2344. refreshJoinChannelText();
  2345. }, _list->lifetime());
  2346. } else {
  2347. refreshJoinChannelText();
  2348. }
  2349. }
  2350. controller()->adaptive().changes(
  2351. ) | rpl::start_with_next([=] {
  2352. _history->forceFullResize();
  2353. if (_migrated) {
  2354. _migrated->forceFullResize();
  2355. }
  2356. updateHistoryGeometry();
  2357. update();
  2358. }, _list->lifetime());
  2359. if (_chooseForReport && _chooseForReport->active) {
  2360. _list->setChooseReportReason(_chooseForReport->reportInput);
  2361. }
  2362. updateTopBarChooseForReport();
  2363. _updateHistoryItems.cancel();
  2364. setupTranslateBar();
  2365. setupPinnedTracker();
  2366. setupGroupCallBar();
  2367. setupRequestsBar();
  2368. checkMessagesTTL();
  2369. if (_history->scrollTopItem
  2370. || (_migrated && _migrated->scrollTopItem)
  2371. || _history->isReadyFor(_showAtMsgId)) {
  2372. historyLoaded();
  2373. } else {
  2374. firstLoadMessages();
  2375. doneShow();
  2376. }
  2377. handlePeerUpdate();
  2378. session().local().readDraftsWithCursors(_history);
  2379. if (!applyDraft()) {
  2380. clearFieldText();
  2381. }
  2382. checkCharsCount();
  2383. _send->finishAnimating();
  2384. updateControlsGeometry();
  2385. if (const auto user = _peer->asUser()) {
  2386. if (const auto &info = user->botInfo) {
  2387. if (startBot
  2388. || (!_history->isEmpty() && clearMaybeSendStart())) {
  2389. if (startBot && wasState.key) {
  2390. info->inlineReturnTo = wasState;
  2391. }
  2392. sendBotStartCommand();
  2393. }
  2394. }
  2395. }
  2396. if (!_history->folderKnown()) {
  2397. session().data().histories().requestDialogEntry(_history);
  2398. }
  2399. // Must be done before unreadCountUpdated(), or we auto-close.
  2400. if (_history->unreadMark()) {
  2401. session().data().histories().changeDialogUnreadMark(
  2402. _history,
  2403. false);
  2404. }
  2405. if (_migrated && _migrated->unreadMark()) {
  2406. session().data().histories().changeDialogUnreadMark(
  2407. _migrated,
  2408. false);
  2409. }
  2410. unreadCountUpdated(); // set _historyDown badge.
  2411. showAboutTopPromotion();
  2412. if (!session().sponsoredMessages().isTopBarFor(_history)) {
  2413. _scroll->setTrackingContent(false);
  2414. const auto checkState = [=] {
  2415. using State = Data::SponsoredMessages::State;
  2416. const auto state = session().sponsoredMessages().state(
  2417. _history);
  2418. _sponsoredMessagesStateKnown = (state != State::None);
  2419. if (state == State::AppendToEnd) {
  2420. _scroll->setTrackingContent(
  2421. session().sponsoredMessages().canHaveFor(_history));
  2422. } else if (state == State::InjectToMiddle) {
  2423. injectSponsoredMessages();
  2424. } else if (state == State::AppendToTopBar) {
  2425. }
  2426. };
  2427. const auto history = _history;
  2428. session().sponsoredMessages().request(
  2429. _history,
  2430. crl::guard(this, [=, this] {
  2431. if (history == _history) {
  2432. checkState();
  2433. }
  2434. }));
  2435. checkState();
  2436. } else {
  2437. requestSponsoredMessageBar();
  2438. }
  2439. } else {
  2440. _chooseForReport = nullptr;
  2441. refreshTopBarActiveChat();
  2442. updateTopBarSelection();
  2443. checkMessagesTTL();
  2444. clearFieldText();
  2445. doneShow();
  2446. }
  2447. updateForwarding();
  2448. updateOverStates(mapFromGlobal(QCursor::pos()));
  2449. if (_history) {
  2450. const auto msgId = (_showAtMsgId == ShowAtTheEndMsgId)
  2451. ? ShowAtUnreadMsgId
  2452. : _showAtMsgId;
  2453. controller()->setActiveChatEntry({
  2454. _history,
  2455. FullMsgId(_history->peer->id, msgId) });
  2456. }
  2457. update();
  2458. controller()->floatPlayerAreaUpdated();
  2459. session().data().itemVisibilitiesUpdated();
  2460. crl::on_main(this, [=] { controller()->widget()->setInnerFocus(); });
  2461. }
  2462. void HistoryWidget::setHistory(History *history) {
  2463. if (_history == history) {
  2464. return;
  2465. }
  2466. const auto was = _attachBotsMenu && _history && _history->peer->isUser();
  2467. const auto now = _attachBotsMenu && history && history->peer->isUser();
  2468. if (was && !now) {
  2469. _attachToggle->removeEventFilter(_attachBotsMenu.get());
  2470. _attachBotsMenu->hideFast();
  2471. } else if (now && !was && !ChatHelpers::ShowPanelOnClick()) {
  2472. _attachToggle->installEventFilter(_attachBotsMenu.get());
  2473. }
  2474. const auto unloadHeavyViewParts = [](History *history) {
  2475. if (history) {
  2476. history->owner().unloadHeavyViewParts(
  2477. history->delegateMixin()->delegate());
  2478. history->forceFullResize();
  2479. }
  2480. };
  2481. if (_history) {
  2482. unregisterDraftSources();
  2483. clearAllLoadRequests();
  2484. clearSupportPreloadRequest();
  2485. _historySponsoredPreloading.destroy();
  2486. const auto wasHistory = base::take(_history);
  2487. const auto wasMigrated = base::take(_migrated);
  2488. unloadHeavyViewParts(wasHistory);
  2489. unloadHeavyViewParts(wasMigrated);
  2490. }
  2491. if (history) {
  2492. _history = history;
  2493. _migrated = _history ? _history->migrateFrom() : nullptr;
  2494. registerDraftSource();
  2495. if (_history) {
  2496. setupPreview();
  2497. } else {
  2498. _previewDrawPreview = nullptr;
  2499. _preview = nullptr;
  2500. }
  2501. }
  2502. refreshAttachBotsMenu();
  2503. }
  2504. void HistoryWidget::setupPreview() {
  2505. Expects(_history != nullptr);
  2506. using namespace HistoryView::Controls;
  2507. _preview = std::make_unique<WebpageProcessor>(_history, _field);
  2508. _preview->repaintRequests() | rpl::start_with_next([=] {
  2509. updateField();
  2510. }, _preview->lifetime());
  2511. _preview->parsedValue(
  2512. ) | rpl::start_with_next([=](WebpageParsed value) {
  2513. _previewTitle.setText(
  2514. st::msgNameStyle,
  2515. value.title,
  2516. Ui::NameTextOptions());
  2517. _previewDescription.setText(
  2518. st::defaultTextStyle,
  2519. value.description,
  2520. Ui::DialogTextOptions());
  2521. const auto changed = (!_previewDrawPreview != !value.drawPreview);
  2522. _previewDrawPreview = value.drawPreview;
  2523. if (changed) {
  2524. updateControlsGeometry();
  2525. updateControlsVisibility();
  2526. }
  2527. updateField();
  2528. }, _preview->lifetime());
  2529. }
  2530. void HistoryWidget::injectSponsoredMessages() const {
  2531. session().sponsoredMessages().inject(
  2532. _history,
  2533. _showAtMsgId,
  2534. _scroll->height() * 2,
  2535. _scroll->width());
  2536. }
  2537. void HistoryWidget::refreshAttachBotsMenu() {
  2538. _attachBotsMenu = nullptr;
  2539. if (!_history) {
  2540. return;
  2541. }
  2542. _attachBotsMenu = InlineBots::MakeAttachBotsMenu(
  2543. this,
  2544. controller(),
  2545. _history->peer,
  2546. [=] { return prepareSendAction({}); },
  2547. [=](bool compress) { chooseAttach(compress); });
  2548. if (!_attachBotsMenu) {
  2549. return;
  2550. }
  2551. _attachBotsMenu->setOrigin(
  2552. Ui::PanelAnimation::Origin::BottomLeft);
  2553. if (!ChatHelpers::ShowPanelOnClick()) {
  2554. _attachToggle->installEventFilter(_attachBotsMenu.get());
  2555. }
  2556. _attachBotsMenu->heightValue(
  2557. ) | rpl::start_with_next([=] {
  2558. moveFieldControls();
  2559. }, _attachBotsMenu->lifetime());
  2560. }
  2561. void HistoryWidget::unregisterDraftSources() {
  2562. if (!_history) {
  2563. return;
  2564. }
  2565. session().local().unregisterDraftSource(
  2566. _history,
  2567. Data::DraftKey::Local({}));
  2568. session().local().unregisterDraftSource(
  2569. _history,
  2570. Data::DraftKey::LocalEdit({}));
  2571. }
  2572. void HistoryWidget::registerDraftSource() {
  2573. if (!_history) {
  2574. return;
  2575. }
  2576. const auto peerId = _history->peer->id;
  2577. const auto editMsgId = _editMsgId;
  2578. const auto draft = [=] {
  2579. return Storage::MessageDraft{
  2580. (editMsgId
  2581. ? FullReplyTo{ FullMsgId(peerId, editMsgId) }
  2582. : _replyTo),
  2583. _field->getTextWithTags(),
  2584. _preview->draft(),
  2585. };
  2586. };
  2587. auto draftSource = Storage::MessageDraftSource{
  2588. .draft = draft,
  2589. .cursor = [=] { return MessageCursor(_field); },
  2590. };
  2591. session().local().registerDraftSource(
  2592. _history,
  2593. (editMsgId
  2594. ? Data::DraftKey::LocalEdit({})
  2595. : Data::DraftKey::Local({})),
  2596. std::move(draftSource));
  2597. }
  2598. void HistoryWidget::setEditMsgId(MsgId msgId) {
  2599. unregisterDraftSources();
  2600. _editMsgId = msgId;
  2601. if (!msgId) {
  2602. _mediaEditManager.cancel();
  2603. _canReplaceMedia = _canAddMedia = false;
  2604. if (_preview) {
  2605. _preview->setDisabled(false);
  2606. }
  2607. }
  2608. if (_history) {
  2609. refreshSendAsToggle();
  2610. orderWidgets();
  2611. }
  2612. registerDraftSource();
  2613. }
  2614. void HistoryWidget::clearDelayedShowAt() {
  2615. _delayedShowAtMsgId = -1;
  2616. clearDelayedShowAtRequest();
  2617. }
  2618. void HistoryWidget::clearDelayedShowAtRequest() {
  2619. Expects(_history != nullptr);
  2620. if (_delayedShowAtRequest) {
  2621. _history->owner().histories().cancelRequest(_delayedShowAtRequest);
  2622. _delayedShowAtRequest = 0;
  2623. }
  2624. }
  2625. void HistoryWidget::clearSupportPreloadRequest() {
  2626. Expects(_history != nullptr);
  2627. if (_supportPreloadRequest) {
  2628. auto &histories = _history->owner().histories();
  2629. histories.cancelRequest(_supportPreloadRequest);
  2630. _supportPreloadRequest = 0;
  2631. }
  2632. }
  2633. void HistoryWidget::clearAllLoadRequests() {
  2634. Expects(_history != nullptr);
  2635. auto &histories = _history->owner().histories();
  2636. clearDelayedShowAtRequest();
  2637. if (_firstLoadRequest) {
  2638. histories.cancelRequest(_firstLoadRequest);
  2639. _firstLoadRequest = 0;
  2640. }
  2641. if (_preloadRequest) {
  2642. histories.cancelRequest(_preloadRequest);
  2643. _preloadRequest = 0;
  2644. }
  2645. if (_preloadDownRequest) {
  2646. histories.cancelRequest(_preloadDownRequest);
  2647. _preloadDownRequest = 0;
  2648. }
  2649. }
  2650. bool HistoryWidget::updateReplaceMediaButton() {
  2651. if (!_canReplaceMedia && !_canAddMedia) {
  2652. const auto result = (_replaceMedia != nullptr);
  2653. _replaceMedia.destroy();
  2654. return result;
  2655. } else if (_replaceMedia) {
  2656. return false;
  2657. }
  2658. _replaceMedia.create(
  2659. this,
  2660. _canReplaceMedia ? st::historyReplaceMedia : st::historyAddMedia);
  2661. const auto hideDuration = st::historyReplaceMedia.ripple.hideDuration;
  2662. _replaceMedia->setClickedCallback([=] {
  2663. base::call_delayed(hideDuration, this, [=] {
  2664. EditCaptionBox::StartMediaReplace(
  2665. controller(),
  2666. { _history->peer->id, _editMsgId },
  2667. _field->getTextWithTags(),
  2668. _mediaEditManager.spoilered(),
  2669. _mediaEditManager.invertCaption(),
  2670. crl::guard(_list, [=] { cancelEdit(); }));
  2671. });
  2672. });
  2673. return true;
  2674. }
  2675. void HistoryWidget::updateFieldSubmitSettings() {
  2676. const auto settings = _isInlineBot
  2677. ? Ui::InputField::SubmitSettings::None
  2678. : Core::App().settings().sendSubmitWay();
  2679. _field->setSubmitSettings(settings);
  2680. }
  2681. void HistoryWidget::updateNotifyControls() {
  2682. if (!_peer || (!_peer->isChannel() && !_peer->isRepliesChat() && !_peer->isVerifyCodes())) {
  2683. return;
  2684. }
  2685. _muteUnmute->setText((_history->muted()
  2686. ? tr::lng_channel_unmute(tr::now)
  2687. : tr::lng_channel_mute(tr::now)).toUpper());
  2688. if (!session().data().notifySettings().silentPostsUnknown(_peer)) {
  2689. if (_silent) {
  2690. _silent->setChecked(
  2691. session().data().notifySettings().silentPosts(_peer));
  2692. updateFieldPlaceholder();
  2693. } else if (hasSilentToggle()) {
  2694. refreshSilentToggle();
  2695. updateControlsVisibility();
  2696. updateControlsGeometry();
  2697. }
  2698. }
  2699. }
  2700. void HistoryWidget::refreshSilentToggle() {
  2701. if (!_silent && hasSilentToggle()) {
  2702. _silent.create(this, _peer->asChannel());
  2703. orderWidgets();
  2704. } else if (_silent && !hasSilentToggle()) {
  2705. _silent.destroy();
  2706. }
  2707. }
  2708. void HistoryWidget::setupFastButtonMode() {
  2709. const auto field = _field->rawTextEdit();
  2710. base::install_event_filter(field, [=](not_null<QEvent*> e) {
  2711. if (e->type() != QEvent::KeyPress
  2712. || !_history
  2713. || !FastButtonsMode()
  2714. || !session().fastButtonsBots().enabled(_history->peer)
  2715. || !_field->getLastText().isEmpty()) {
  2716. return base::EventFilterResult::Continue;
  2717. }
  2718. const auto k = static_cast<QKeyEvent*>(e.get());
  2719. const auto key = k->key();
  2720. if (key < Qt::Key_1 || key > Qt::Key_9 || k->modifiers()) {
  2721. return base::EventFilterResult::Continue;
  2722. }
  2723. const auto item = _history ? _history->lastMessage() : nullptr;
  2724. const auto markup = item ? item->inlineReplyKeyboard() : nullptr;
  2725. const auto link = markup
  2726. ? markup->getLinkByIndex(key - Qt::Key_1)
  2727. : nullptr;
  2728. if (!link) {
  2729. return base::EventFilterResult::Continue;
  2730. }
  2731. ActivateClickHandler(window(), link, {
  2732. Qt::LeftButton,
  2733. QVariant::fromValue(ClickHandlerContext{
  2734. .itemId = item->fullId(),
  2735. .sessionWindow = base::make_weak(controller()),
  2736. }),
  2737. });
  2738. return base::EventFilterResult::Cancel;
  2739. });
  2740. }
  2741. void HistoryWidget::setupScheduledToggle() {
  2742. controller()->activeChatValue(
  2743. ) | rpl::map([=](Dialogs::Key key) -> rpl::producer<> {
  2744. if (const auto history = key.history()) {
  2745. return session().scheduledMessages().updates(history);
  2746. } else if (const auto topic = key.topic()) {
  2747. return session().scheduledMessages().updates(
  2748. topic->owningHistory());
  2749. }
  2750. return rpl::never<rpl::empty_value>();
  2751. }) | rpl::flatten_latest(
  2752. ) | rpl::start_with_next([=] {
  2753. refreshScheduledToggle();
  2754. updateControlsVisibility();
  2755. updateControlsGeometry();
  2756. }, lifetime());
  2757. }
  2758. void HistoryWidget::refreshScheduledToggle() {
  2759. const auto has = _history
  2760. && _canSendMessages
  2761. && (session().scheduledMessages().count(_history) > 0);
  2762. if (!_scheduled && has) {
  2763. _scheduled.create(this, st::historyScheduledToggle);
  2764. _scheduled->show();
  2765. _scheduled->addClickHandler([=] {
  2766. controller()->showSection(
  2767. std::make_shared<HistoryView::ScheduledMemento>(_history));
  2768. });
  2769. orderWidgets(); // Raise drag areas to the top.
  2770. } else if (_scheduled && !has) {
  2771. _scheduled.destroy();
  2772. }
  2773. }
  2774. void HistoryWidget::setupSendAsToggle() {
  2775. session().sendAsPeers().updated(
  2776. ) | rpl::filter([=](not_null<PeerData*> peer) {
  2777. return (peer == _peer);
  2778. }) | rpl::start_with_next([=] {
  2779. refreshSendAsToggle();
  2780. updateControlsVisibility();
  2781. updateControlsGeometry();
  2782. orderWidgets();
  2783. }, lifetime());
  2784. }
  2785. void HistoryWidget::refreshSendAsToggle() {
  2786. Expects(_peer != nullptr);
  2787. if (_editMsgId || !session().sendAsPeers().shouldChoose(_peer)) {
  2788. _sendAs.destroy();
  2789. return;
  2790. } else if (_sendAs) {
  2791. return;
  2792. }
  2793. _sendAs.create(this, st::sendAsButton);
  2794. Ui::SetupSendAsButton(_sendAs.data(), controller());
  2795. }
  2796. bool HistoryWidget::contentOverlapped(const QRect &globalRect) {
  2797. return (_attachDragAreas.document->overlaps(globalRect)
  2798. || _attachDragAreas.photo->overlaps(globalRect)
  2799. || (_autocomplete && _autocomplete->overlaps(globalRect))
  2800. || (_tabbedPanel && _tabbedPanel->overlaps(globalRect))
  2801. || (_inlineResults && _inlineResults->overlaps(globalRect)));
  2802. }
  2803. bool HistoryWidget::canWriteMessage() const {
  2804. return _history
  2805. && _canSendMessages
  2806. && !isBlocked()
  2807. && !isJoinChannel()
  2808. && !isMuteUnmute()
  2809. && !isBotStart()
  2810. && !isSearching();
  2811. }
  2812. void HistoryWidget::updateControlsVisibility() {
  2813. auto fieldDisabledRemoved = (_fieldDisabled != nullptr);
  2814. const auto hideExtraButtons = _fieldCharsCountManager.isLimitExceeded();
  2815. const auto guard = gsl::finally([&] {
  2816. if (fieldDisabledRemoved) {
  2817. _fieldDisabled = nullptr;
  2818. }
  2819. });
  2820. if (!_showAnimation) {
  2821. _topShadow->setVisible(_peer != nullptr);
  2822. _topBar->setVisible(_peer != nullptr);
  2823. }
  2824. _cornerButtons.updateJumpDownVisibility();
  2825. _cornerButtons.updateUnreadThingsVisibility();
  2826. if (!_history || _showAnimation) {
  2827. hideChildWidgets();
  2828. return;
  2829. }
  2830. if (_firstLoadRequest && !_scroll->isHidden()) {
  2831. if (Ui::InFocusChain(_scroll.data())) {
  2832. // Don't loose focus back to chats list.
  2833. setFocus();
  2834. }
  2835. _scroll->hide();
  2836. } else if (!_firstLoadRequest && _scroll->isHidden()) {
  2837. _scroll->show();
  2838. }
  2839. if (_pinnedBar) {
  2840. _pinnedBar->show();
  2841. }
  2842. if (_sponsoredMessageBar && checkSponsoredMessageBarVisibility()) {
  2843. _sponsoredMessageBar->toggle(true, anim::type::normal);
  2844. }
  2845. if (_translateBar) {
  2846. _translateBar->show();
  2847. }
  2848. if (_groupCallBar) {
  2849. _groupCallBar->show();
  2850. }
  2851. if (_requestsBar) {
  2852. _requestsBar->show();
  2853. }
  2854. if (_paysStatus) {
  2855. _paysStatus->show();
  2856. }
  2857. if (_contactStatus) {
  2858. _contactStatus->show();
  2859. }
  2860. if (_businessBotStatus) {
  2861. _businessBotStatus->show();
  2862. }
  2863. if (isChoosingTheme()
  2864. || (!editingMessage()
  2865. && (isSearching()
  2866. || isBlocked()
  2867. || isJoinChannel()
  2868. || isMuteUnmute()
  2869. || isBotStart()
  2870. || isReportMessages()))) {
  2871. const auto toggle = [&](Ui::FlatButton *shown) {
  2872. const auto toggleOne = [&](not_null<Ui::FlatButton*> button) {
  2873. if (button.get() != shown) {
  2874. button->hide();
  2875. } else if (button->isHidden()) {
  2876. button->clearState();
  2877. button->show();
  2878. }
  2879. };
  2880. toggleOne(_reportMessages);
  2881. toggleOne(_joinChannel);
  2882. toggleOne(_muteUnmute);
  2883. toggleOne(_botStart);
  2884. toggleOne(_unblock);
  2885. };
  2886. if (isChoosingTheme()) {
  2887. _chooseTheme->show();
  2888. setInnerFocus();
  2889. toggle(nullptr);
  2890. } else if (isReportMessages()) {
  2891. toggle(_reportMessages);
  2892. } else if (isBlocked()) {
  2893. toggle(_unblock);
  2894. } else if (isJoinChannel()) {
  2895. toggle(_joinChannel);
  2896. } else if (isMuteUnmute()) {
  2897. toggle(_muteUnmute);
  2898. } else if (isBotStart()) {
  2899. toggle(_botStart);
  2900. }
  2901. _kbShown = false;
  2902. if (_autocomplete) {
  2903. _autocomplete->hide();
  2904. }
  2905. if (_supportAutocomplete) {
  2906. _supportAutocomplete->hide();
  2907. }
  2908. _send->hide();
  2909. if (_silent) {
  2910. _silent->hide();
  2911. }
  2912. if (_scheduled) {
  2913. _scheduled->hide();
  2914. }
  2915. if (_ttlInfo) {
  2916. _ttlInfo->hide();
  2917. }
  2918. if (_sendAs) {
  2919. _sendAs->hide();
  2920. }
  2921. _kbScroll->hide();
  2922. _fieldBarCancel->hide();
  2923. _attachToggle->hide();
  2924. if (_replaceMedia) {
  2925. _replaceMedia->hide();
  2926. }
  2927. _tabbedSelectorToggle->hide();
  2928. _botKeyboardShow->hide();
  2929. _botKeyboardHide->hide();
  2930. _botCommandStart->hide();
  2931. if (_botMenu.button) {
  2932. _botMenu.button->hide();
  2933. }
  2934. if (_tabbedPanel) {
  2935. _tabbedPanel->hide();
  2936. }
  2937. if (_voiceRecordBar) {
  2938. _voiceRecordBar->hideFast();
  2939. }
  2940. if (_inlineResults) {
  2941. _inlineResults->hide();
  2942. }
  2943. if (_sendRestriction) {
  2944. _sendRestriction->hide();
  2945. }
  2946. hideFieldIfVisible();
  2947. } else if (editingMessage() || _canSendMessages) {
  2948. if (_autocomplete) {
  2949. _autocomplete->requestRefresh();
  2950. }
  2951. _unblock->hide();
  2952. _botStart->hide();
  2953. _joinChannel->hide();
  2954. _muteUnmute->hide();
  2955. _reportMessages->hide();
  2956. _send->show();
  2957. updateSendButtonType();
  2958. if (_canSendTexts || _editMsgId) {
  2959. _field->show();
  2960. } else {
  2961. fieldDisabledRemoved = false;
  2962. if (!_fieldDisabled) {
  2963. _fieldDisabled = CreateDisabledFieldView(this, _peer);
  2964. orderWidgets();
  2965. updateControlsGeometry();
  2966. update();
  2967. }
  2968. _fieldDisabled->show();
  2969. hideFieldIfVisible();
  2970. }
  2971. if (_kbShown) {
  2972. _kbScroll->show();
  2973. _tabbedSelectorToggle->hide();
  2974. showKeyboardHideButton();
  2975. _botKeyboardShow->hide();
  2976. _botCommandStart->hide();
  2977. } else if (_kbReplyTo) {
  2978. _kbScroll->hide();
  2979. _tabbedSelectorToggle->show();
  2980. _botKeyboardHide->hide();
  2981. _botKeyboardShow->hide();
  2982. _botCommandStart->hide();
  2983. } else {
  2984. _kbScroll->hide();
  2985. _tabbedSelectorToggle->show();
  2986. _botKeyboardHide->hide();
  2987. if (_keyboard->hasMarkup()) {
  2988. _botKeyboardShow->show();
  2989. _botCommandStart->hide();
  2990. } else {
  2991. _botKeyboardShow->hide();
  2992. _botCommandStart->setVisible(_cmdStartShown);
  2993. }
  2994. }
  2995. if (_replaceMedia) {
  2996. _replaceMedia->show();
  2997. _attachToggle->hide();
  2998. } else {
  2999. _attachToggle->show();
  3000. }
  3001. if (_botMenu.button) {
  3002. _botMenu.button->show();
  3003. }
  3004. if (_sendRestriction) {
  3005. _sendRestriction->hide();
  3006. }
  3007. {
  3008. auto rightButtonsChanged = false;
  3009. if (_silent) {
  3010. const auto was = _silent->isVisible();
  3011. const auto now = (!_editMsgId) && (!hideExtraButtons);
  3012. if (was != now) {
  3013. _silent->setVisible(now);
  3014. rightButtonsChanged = true;
  3015. }
  3016. }
  3017. if (_scheduled) {
  3018. const auto was = _scheduled->isVisible();
  3019. const auto now = (!_editMsgId) && (!hideExtraButtons);
  3020. if (was != now) {
  3021. _scheduled->setVisible(now);
  3022. rightButtonsChanged = true;
  3023. }
  3024. }
  3025. if (_ttlInfo) {
  3026. const auto was = _ttlInfo->isVisible();
  3027. const auto now = (!_editMsgId) && (!hideExtraButtons);
  3028. if (was != now) {
  3029. _ttlInfo->setVisible(now);
  3030. rightButtonsChanged = true;
  3031. }
  3032. }
  3033. if (rightButtonsChanged) {
  3034. updateFieldSize();
  3035. }
  3036. }
  3037. if (_sendAs) {
  3038. _sendAs->show();
  3039. }
  3040. updateFieldPlaceholder();
  3041. if (_editMsgId
  3042. || _replyTo
  3043. || readyToForward()
  3044. || _previewDrawPreview
  3045. || _kbReplyTo) {
  3046. if (_fieldBarCancel->isHidden()) {
  3047. _fieldBarCancel->show();
  3048. updateControlsGeometry();
  3049. update();
  3050. }
  3051. } else {
  3052. _fieldBarCancel->hide();
  3053. }
  3054. } else {
  3055. if (_autocomplete) {
  3056. _autocomplete->hide();
  3057. }
  3058. if (_supportAutocomplete) {
  3059. _supportAutocomplete->hide();
  3060. }
  3061. _send->hide();
  3062. _unblock->hide();
  3063. _botStart->hide();
  3064. _joinChannel->hide();
  3065. _muteUnmute->hide();
  3066. _reportMessages->hide();
  3067. _attachToggle->hide();
  3068. if (_silent) {
  3069. _silent->hide();
  3070. }
  3071. if (_scheduled) {
  3072. _scheduled->hide();
  3073. }
  3074. if (_ttlInfo) {
  3075. _ttlInfo->hide();
  3076. }
  3077. if (_sendAs) {
  3078. _sendAs->hide();
  3079. }
  3080. if (_botMenu.button) {
  3081. _botMenu.button->hide();
  3082. }
  3083. _kbScroll->hide();
  3084. if (_replyTo || readyToForward() || _kbReplyTo) {
  3085. if (_fieldBarCancel->isHidden()) {
  3086. _fieldBarCancel->show();
  3087. updateControlsGeometry();
  3088. update();
  3089. }
  3090. } else {
  3091. _fieldBarCancel->hide();
  3092. }
  3093. _tabbedSelectorToggle->hide();
  3094. _botKeyboardShow->hide();
  3095. _botKeyboardHide->hide();
  3096. _botCommandStart->hide();
  3097. if (_tabbedPanel) {
  3098. _tabbedPanel->hide();
  3099. }
  3100. if (_voiceRecordBar) {
  3101. _voiceRecordBar->hideFast();
  3102. }
  3103. if (_composeSearch) {
  3104. _composeSearch->hideAnimated();
  3105. }
  3106. if (_inlineResults) {
  3107. _inlineResults->hide();
  3108. }
  3109. if (_sendRestriction) {
  3110. _sendRestriction->show();
  3111. }
  3112. _kbScroll->hide();
  3113. hideFieldIfVisible();
  3114. }
  3115. //checkTabbedSelectorToggleTooltip();
  3116. updateMouseTracking();
  3117. }
  3118. void HistoryWidget::hideFieldIfVisible() {
  3119. if (_field->isHidden()) {
  3120. return;
  3121. } else if (InFocusChain(_field)) {
  3122. setFocus();
  3123. }
  3124. _field->hide();
  3125. updateControlsGeometry();
  3126. update();
  3127. }
  3128. void HistoryWidget::showAboutTopPromotion() {
  3129. Expects(_history != nullptr);
  3130. Expects(_list != nullptr);
  3131. if (!_history->useTopPromotion() || _history->topPromotionAboutShown()) {
  3132. return;
  3133. }
  3134. _history->markTopPromotionAboutShown();
  3135. const auto type = _history->topPromotionType();
  3136. const auto custom = type.isEmpty()
  3137. ? QString()
  3138. : Lang::GetNonDefaultValue(kPsaAboutPrefix + type.toUtf8());
  3139. const auto text = type.isEmpty()
  3140. ? tr::lng_proxy_sponsor_about(tr::now, Ui::Text::RichLangValue)
  3141. : custom.isEmpty()
  3142. ? tr::lng_about_psa_default(tr::now, Ui::Text::RichLangValue)
  3143. : Ui::Text::RichLangValue(custom);
  3144. showInfoTooltip(text, nullptr);
  3145. }
  3146. void HistoryWidget::updateMouseTracking() {
  3147. const auto trackMouse = !_fieldBarCancel->isHidden();
  3148. setMouseTracking(trackMouse);
  3149. }
  3150. void HistoryWidget::destroyUnreadBar() {
  3151. if (_history) _history->destroyUnreadBar();
  3152. if (_migrated) _migrated->destroyUnreadBar();
  3153. }
  3154. void HistoryWidget::destroyUnreadBarOnClose() {
  3155. if (!_history || !_historyInited) {
  3156. return;
  3157. } else if (_scroll->scrollTop() == _scroll->scrollTopMax()) {
  3158. destroyUnreadBar();
  3159. return;
  3160. }
  3161. const auto top = unreadBarTop();
  3162. if (top && *top < _scroll->scrollTop()) {
  3163. destroyUnreadBar();
  3164. return;
  3165. }
  3166. }
  3167. void HistoryWidget::newItemAdded(not_null<HistoryItem*> item) {
  3168. if (_history != item->history()
  3169. || !_historyInited
  3170. || item->isScheduled()) {
  3171. return;
  3172. }
  3173. if (item->isSponsored()) {
  3174. if (const auto view = item->mainView()) {
  3175. view->resizeGetHeight(width());
  3176. updateHistoryGeometry(
  3177. false,
  3178. true,
  3179. { ScrollChangeNoJumpToBottom, 0 });
  3180. }
  3181. return;
  3182. }
  3183. // If we get here in non-resized state we can't rely on results of
  3184. // markingMessagesRead() and mark chat as read.
  3185. // If we receive N messages being not at bottom:
  3186. // - on first message we set unreadcount += 1, firstUnreadMessage.
  3187. // - on second we get wrong markingMessagesRead() and read both.
  3188. session().data().sendHistoryChangeNotifications();
  3189. if (item->isSending()) {
  3190. synteticScrollToY(_scroll->scrollTopMax());
  3191. } else if (_scroll->scrollTop() < _scroll->scrollTopMax()) {
  3192. return;
  3193. }
  3194. if (item->showNotification()) {
  3195. destroyUnreadBar();
  3196. if (markingMessagesRead()) {
  3197. if (_list && item->hasUnwatchedEffect()) {
  3198. _list->startEffectOnRead(item);
  3199. }
  3200. if (item->isUnreadMention() && !item->isUnreadMedia()) {
  3201. session().api().markContentsRead(item);
  3202. }
  3203. session().data().histories().readInboxOnNewMessage(item);
  3204. // Also clear possible scheduled messages notifications.
  3205. // Side-effect: Also clears all notifications from forum topics.
  3206. Core::App().notifications().clearFromHistory(_history);
  3207. }
  3208. }
  3209. const auto view = item->mainView();
  3210. if (!view) {
  3211. return;
  3212. } else if (anim::Disabled()) {
  3213. if (!On(PowerSaving::kChatBackground)) {
  3214. // Strange case of disabled animations, but enabled bg rotation.
  3215. if (item->out() || _history->peer->isSelf()) {
  3216. _list->theme()->rotateComplexGradientBackground();
  3217. }
  3218. }
  3219. return;
  3220. }
  3221. _itemRevealPending.emplace(item);
  3222. }
  3223. void HistoryWidget::maybeMarkReactionsRead(not_null<HistoryItem*> item) {
  3224. if (!_historyInited || !_list) {
  3225. return;
  3226. }
  3227. const auto view = item->mainView();
  3228. const auto itemTop = _list->itemTop(view);
  3229. if (itemTop <= 0 || !markingContentsRead()) {
  3230. return;
  3231. }
  3232. const auto reactionCenter
  3233. = view->reactionButtonParameters({}, {}).center.y();
  3234. const auto visibleTop = _scroll->scrollTop();
  3235. const auto visibleBottom = visibleTop + _scroll->height();
  3236. if (itemTop + reactionCenter < visibleTop
  3237. || itemTop + view->height() > visibleBottom) {
  3238. return;
  3239. }
  3240. session().api().markContentsRead(item);
  3241. }
  3242. void HistoryWidget::unreadCountUpdated() {
  3243. if (_history->unreadMark() || (_migrated && _migrated->unreadMark())) {
  3244. crl::on_main(this, [=, history = _history] {
  3245. if (history == _history) {
  3246. closeCurrent();
  3247. }
  3248. });
  3249. } else {
  3250. const auto hideCounter = _history->isForum()
  3251. || !_history->trackUnreadMessages();
  3252. _cornerButtons.updateJumpDownVisibility(hideCounter
  3253. ? 0
  3254. : _history->chatListBadgesState().unreadCounter);
  3255. }
  3256. }
  3257. void HistoryWidget::closeCurrent() {
  3258. if (controller()->isPrimary()) {
  3259. controller()->showBackFromStack();
  3260. } else {
  3261. controller()->window().close();
  3262. }
  3263. }
  3264. void HistoryWidget::messagesFailed(const MTP::Error &error, int requestId) {
  3265. if (error.type() == u"CHANNEL_PRIVATE"_q
  3266. && _peer->isChannel()
  3267. && _peer->asChannel()->invitePeekExpires()) {
  3268. _peer->asChannel()->privateErrorReceived();
  3269. } else if (error.type() == u"CHANNEL_PRIVATE"_q
  3270. || error.type() == u"CHANNEL_PUBLIC_GROUP_NA"_q
  3271. || error.type() == u"USER_BANNED_IN_CHANNEL"_q) {
  3272. auto was = _peer;
  3273. closeCurrent();
  3274. const auto wasAccount = not_null(&was->account());
  3275. if (const auto primary = Core::App().windowFor(wasAccount)) {
  3276. primary->showToast(was->isMegagroup()
  3277. ? tr::lng_group_not_accessible(tr::now)
  3278. : tr::lng_channel_not_accessible(tr::now));
  3279. }
  3280. return;
  3281. }
  3282. LOG(("RPC Error: %1 %2: %3").arg(
  3283. QString::number(error.code()),
  3284. error.type(),
  3285. error.description()));
  3286. if (_preloadRequest == requestId) {
  3287. _preloadRequest = 0;
  3288. } else if (_preloadDownRequest == requestId) {
  3289. _preloadDownRequest = 0;
  3290. } else if (_firstLoadRequest == requestId) {
  3291. _firstLoadRequest = 0;
  3292. closeCurrent();
  3293. } else if (_delayedShowAtRequest == requestId) {
  3294. _delayedShowAtRequest = 0;
  3295. }
  3296. }
  3297. void HistoryWidget::messagesReceived(
  3298. not_null<PeerData*> peer,
  3299. const MTPmessages_Messages &messages,
  3300. int requestId) {
  3301. Expects(_history != nullptr);
  3302. const auto toMigrated = (peer == _peer->migrateFrom());
  3303. if (peer != _peer && !toMigrated) {
  3304. if (_preloadRequest == requestId) {
  3305. _preloadRequest = 0;
  3306. } else if (_preloadDownRequest == requestId) {
  3307. _preloadDownRequest = 0;
  3308. } else if (_firstLoadRequest == requestId) {
  3309. _firstLoadRequest = 0;
  3310. } else if (_delayedShowAtRequest == requestId) {
  3311. _delayedShowAtRequest = 0;
  3312. }
  3313. return;
  3314. }
  3315. auto count = 0;
  3316. const QVector<MTPMessage> emptyList, *histList = &emptyList;
  3317. switch (messages.type()) {
  3318. case mtpc_messages_messages: {
  3319. auto &d(messages.c_messages_messages());
  3320. _history->owner().processUsers(d.vusers());
  3321. _history->owner().processChats(d.vchats());
  3322. histList = &d.vmessages().v;
  3323. count = histList->size();
  3324. } break;
  3325. case mtpc_messages_messagesSlice: {
  3326. auto &d(messages.c_messages_messagesSlice());
  3327. _history->owner().processUsers(d.vusers());
  3328. _history->owner().processChats(d.vchats());
  3329. histList = &d.vmessages().v;
  3330. count = d.vcount().v;
  3331. } break;
  3332. case mtpc_messages_channelMessages: {
  3333. auto &d(messages.c_messages_channelMessages());
  3334. if (const auto channel = peer->asChannel()) {
  3335. channel->ptsReceived(d.vpts().v);
  3336. channel->processTopics(d.vtopics());
  3337. } else {
  3338. LOG(("API Error: received messages.channelMessages when "
  3339. "no channel was passed! (HistoryWidget::messagesReceived)"));
  3340. }
  3341. _history->owner().processUsers(d.vusers());
  3342. _history->owner().processChats(d.vchats());
  3343. histList = &d.vmessages().v;
  3344. count = d.vcount().v;
  3345. } break;
  3346. case mtpc_messages_messagesNotModified: {
  3347. LOG(("API Error: received messages.messagesNotModified! "
  3348. "(HistoryWidget::messagesReceived)"));
  3349. } break;
  3350. }
  3351. if (_preloadRequest == requestId) {
  3352. addMessagesToFront(peer, *histList);
  3353. _preloadRequest = 0;
  3354. preloadHistoryIfNeeded();
  3355. } else if (_preloadDownRequest == requestId) {
  3356. addMessagesToBack(peer, *histList);
  3357. _preloadDownRequest = 0;
  3358. preloadHistoryIfNeeded();
  3359. if (_history->loadedAtBottom()) {
  3360. checkActivation();
  3361. }
  3362. } else if (_firstLoadRequest == requestId) {
  3363. if (toMigrated) {
  3364. _history->clear(History::ClearType::Unload);
  3365. } else if (_migrated) {
  3366. _migrated->clear(History::ClearType::Unload);
  3367. }
  3368. addMessagesToFront(peer, *histList);
  3369. _firstLoadRequest = 0;
  3370. if (_history->loadedAtTop() && _history->isEmpty() && count > 0) {
  3371. firstLoadMessages();
  3372. return;
  3373. }
  3374. historyLoaded();
  3375. injectSponsoredMessages();
  3376. } else if (_delayedShowAtRequest == requestId) {
  3377. if (toMigrated) {
  3378. _history->clear(History::ClearType::Unload);
  3379. } else if (_migrated) {
  3380. _migrated->clear(History::ClearType::Unload);
  3381. }
  3382. clearAllLoadRequests();
  3383. _firstLoadRequest = -1; // hack - don't updateListSize yet
  3384. _history->getReadyFor(_delayedShowAtMsgId);
  3385. if (_history->isEmpty()) {
  3386. addMessagesToFront(peer, *histList);
  3387. }
  3388. _firstLoadRequest = 0;
  3389. if (_history->loadedAtTop()
  3390. && _history->isEmpty()
  3391. && count > 0) {
  3392. firstLoadMessages();
  3393. return;
  3394. }
  3395. const auto skipId = (_migrated && _delayedShowAtMsgId < 0)
  3396. ? FullMsgId(_migrated->peer->id, -_delayedShowAtMsgId)
  3397. : (_delayedShowAtMsgId > 0)
  3398. ? FullMsgId(_history->peer->id, _delayedShowAtMsgId)
  3399. : FullMsgId();
  3400. if (skipId) {
  3401. _cornerButtons.skipReplyReturn(skipId);
  3402. }
  3403. _delayedShowAtRequest = 0;
  3404. setMsgId(_delayedShowAtMsgId, _delayedShowAtMsgParams);
  3405. historyLoaded();
  3406. }
  3407. if (session().supportMode()) {
  3408. crl::on_main(this, [=] { checkSupportPreload(); });
  3409. }
  3410. }
  3411. void HistoryWidget::historyLoaded() {
  3412. _historyInited = false;
  3413. doneShow();
  3414. }
  3415. bool HistoryWidget::clearMaybeSendStart() {
  3416. if (!_showAndMaybeSendStart || !_history) {
  3417. return false;
  3418. } else if (!_history->peer->isFullLoaded()) {
  3419. _history->peer->updateFull();
  3420. return false;
  3421. }
  3422. _showAndMaybeSendStart = false;
  3423. if (const auto user = _history ? _history->peer->asUser() : nullptr) {
  3424. if (user->blockStatus() == PeerData::BlockStatus::NotBlocked) {
  3425. if (const auto info = user->botInfo.get()) {
  3426. if (!info->startToken.isEmpty()) {
  3427. return true;
  3428. }
  3429. }
  3430. }
  3431. }
  3432. return false;
  3433. }
  3434. void HistoryWidget::windowShown() {
  3435. updateControlsGeometry();
  3436. }
  3437. bool HistoryWidget::markingMessagesRead() const {
  3438. return markingContentsRead() && !session().supportMode();
  3439. }
  3440. bool HistoryWidget::markingContentsRead() const {
  3441. return _history
  3442. && _list
  3443. && _historyInited
  3444. && !_firstLoadRequest
  3445. && !_delayedShowAtRequest
  3446. && !_showAnimation
  3447. && controller()->widget()->markingAsRead();
  3448. }
  3449. void HistoryWidget::checkActivation() {
  3450. if (_list) {
  3451. _list->checkActivation();
  3452. }
  3453. }
  3454. void HistoryWidget::firstLoadMessages() {
  3455. if (!_history || _firstLoadRequest) {
  3456. return;
  3457. }
  3458. auto from = _history;
  3459. auto offsetId = MsgId();
  3460. auto offset = 0;
  3461. auto loadCount = kMessagesPerPage;
  3462. if (_showAtMsgId == ShowAtUnreadMsgId) {
  3463. if (const auto around = _migrated ? _migrated->loadAroundId() : 0) {
  3464. _history->getReadyFor(_showAtMsgId);
  3465. from = _migrated;
  3466. offset = -loadCount / 2;
  3467. offsetId = around;
  3468. } else if (const auto around = _history->loadAroundId()) {
  3469. _history->getReadyFor(_showAtMsgId);
  3470. offset = -loadCount / 2;
  3471. offsetId = around;
  3472. } else {
  3473. _history->getReadyFor(ShowAtTheEndMsgId);
  3474. }
  3475. } else if (_showAtMsgId == ShowAtTheEndMsgId) {
  3476. _history->getReadyFor(_showAtMsgId);
  3477. loadCount = kMessagesPerPageFirst;
  3478. } else if (_showAtMsgId > 0) {
  3479. _history->getReadyFor(_showAtMsgId);
  3480. offset = -loadCount / 2;
  3481. offsetId = _showAtMsgId;
  3482. } else if (_showAtMsgId < 0 && _history->peer->isChannel()) {
  3483. if (_showAtMsgId < 0 && -_showAtMsgId < ServerMaxMsgId && _migrated) {
  3484. _history->getReadyFor(_showAtMsgId);
  3485. from = _migrated;
  3486. offset = -loadCount / 2;
  3487. offsetId = -_showAtMsgId;
  3488. } else if (_showAtMsgId == SwitchAtTopMsgId) {
  3489. _history->getReadyFor(_showAtMsgId);
  3490. }
  3491. }
  3492. const auto offsetDate = 0;
  3493. const auto maxId = 0;
  3494. const auto minId = 0;
  3495. const auto historyHash = uint64(0);
  3496. const auto history = from;
  3497. const auto type = Data::Histories::RequestType::History;
  3498. auto &histories = history->owner().histories();
  3499. _firstLoadRequest = histories.sendRequest(history, type, [=](
  3500. Fn<void()> finish) {
  3501. return history->session().api().request(MTPmessages_GetHistory(
  3502. history->peer->input,
  3503. MTP_int(offsetId),
  3504. MTP_int(offsetDate),
  3505. MTP_int(offset),
  3506. MTP_int(loadCount),
  3507. MTP_int(maxId),
  3508. MTP_int(minId),
  3509. MTP_long(historyHash)
  3510. )).done([=](const MTPmessages_Messages &result) {
  3511. messagesReceived(history->peer, result, _firstLoadRequest);
  3512. finish();
  3513. }).fail([=](const MTP::Error &error) {
  3514. messagesFailed(error, _firstLoadRequest);
  3515. finish();
  3516. }).send();
  3517. });
  3518. }
  3519. void HistoryWidget::loadMessages() {
  3520. if (!_history || _preloadRequest) {
  3521. return;
  3522. }
  3523. if (_history->isEmpty() && _migrated && _migrated->isEmpty()) {
  3524. return firstLoadMessages();
  3525. }
  3526. auto loadMigrated = _migrated
  3527. && (_history->isEmpty()
  3528. || _history->loadedAtTop()
  3529. || (!_migrated->isEmpty() && !_migrated->loadedAtBottom()));
  3530. const auto from = loadMigrated ? _migrated : _history;
  3531. if (from->loadedAtTop()) {
  3532. return;
  3533. }
  3534. const auto offsetId = from->minMsgId();
  3535. const auto addOffset = 0;
  3536. const auto loadCount = offsetId
  3537. ? kMessagesPerPage
  3538. : kMessagesPerPageFirst;
  3539. const auto offsetDate = 0;
  3540. const auto maxId = 0;
  3541. const auto minId = 0;
  3542. const auto historyHash = uint64(0);
  3543. DEBUG_LOG(("JumpToEnd(%1, %2, %3): Loading up before %4."
  3544. ).arg(_history->peer->name()
  3545. ).arg(_history->inboxReadTillId().bare
  3546. ).arg(Logs::b(_history->loadedAtBottom())
  3547. ).arg(offsetId.bare));
  3548. const auto history = from;
  3549. const auto type = Data::Histories::RequestType::History;
  3550. auto &histories = history->owner().histories();
  3551. _preloadRequest = histories.sendRequest(history, type, [=](
  3552. Fn<void()> finish) {
  3553. return history->session().api().request(MTPmessages_GetHistory(
  3554. history->peer->input,
  3555. MTP_int(offsetId),
  3556. MTP_int(offsetDate),
  3557. MTP_int(addOffset),
  3558. MTP_int(loadCount),
  3559. MTP_int(maxId),
  3560. MTP_int(minId),
  3561. MTP_long(historyHash)
  3562. )).done([=](const MTPmessages_Messages &result) {
  3563. messagesReceived(history->peer, result, _preloadRequest);
  3564. finish();
  3565. }).fail([=](const MTP::Error &error) {
  3566. messagesFailed(error, _preloadRequest);
  3567. finish();
  3568. }).send();
  3569. });
  3570. }
  3571. void HistoryWidget::loadMessagesDown() {
  3572. if (!_history || _preloadDownRequest) {
  3573. return;
  3574. }
  3575. if (_history->isEmpty() && _migrated && _migrated->isEmpty()) {
  3576. return firstLoadMessages();
  3577. }
  3578. const auto loadMigrated = _migrated
  3579. && !(_migrated->isEmpty()
  3580. || _migrated->loadedAtBottom()
  3581. || (!_history->isEmpty() && !_history->loadedAtTop()));
  3582. const auto from = loadMigrated ? _migrated : _history;
  3583. if (from->loadedAtBottom()) {
  3584. if (_sponsoredMessagesStateKnown) {
  3585. session().sponsoredMessages().request(_history, nullptr);
  3586. }
  3587. return;
  3588. }
  3589. const auto loadCount = kMessagesPerPage;
  3590. auto addOffset = -loadCount;
  3591. auto offsetId = from->maxMsgId();
  3592. if (!offsetId) {
  3593. if (loadMigrated || !_migrated) {
  3594. return;
  3595. }
  3596. ++offsetId;
  3597. ++addOffset;
  3598. }
  3599. const auto offsetDate = 0;
  3600. const auto maxId = 0;
  3601. const auto minId = 0;
  3602. const auto historyHash = uint64(0);
  3603. DEBUG_LOG(("JumpToEnd(%1, %2, %3): Loading down after %4."
  3604. ).arg(_history->peer->name()
  3605. ).arg(_history->inboxReadTillId().bare
  3606. ).arg(Logs::b(_history->loadedAtBottom())
  3607. ).arg(offsetId.bare));
  3608. const auto history = from;
  3609. const auto type = Data::Histories::RequestType::History;
  3610. auto &histories = history->owner().histories();
  3611. _preloadDownRequest = histories.sendRequest(history, type, [=](
  3612. Fn<void()> finish) {
  3613. return history->session().api().request(MTPmessages_GetHistory(
  3614. history->peer->input,
  3615. MTP_int(offsetId + 1),
  3616. MTP_int(offsetDate),
  3617. MTP_int(addOffset),
  3618. MTP_int(loadCount),
  3619. MTP_int(maxId),
  3620. MTP_int(minId),
  3621. MTP_long(historyHash)
  3622. )).done([=](const MTPmessages_Messages &result) {
  3623. messagesReceived(history->peer, result, _preloadDownRequest);
  3624. finish();
  3625. }).fail([=](const MTP::Error &error) {
  3626. messagesFailed(error, _preloadDownRequest);
  3627. finish();
  3628. }).send();
  3629. });
  3630. }
  3631. void HistoryWidget::delayedShowAt(
  3632. MsgId showAtMsgId,
  3633. const Window::SectionShow &params) {
  3634. if (!_history) {
  3635. return;
  3636. }
  3637. _delayedShowAtMsgParams = params;
  3638. if (_delayedShowAtRequest && _delayedShowAtMsgId == showAtMsgId) {
  3639. return;
  3640. }
  3641. clearAllLoadRequests();
  3642. _delayedShowAtMsgId = showAtMsgId;
  3643. DEBUG_LOG(("JumpToEnd(%1, %2, %3): Loading delayed around %4."
  3644. ).arg(_history->peer->name()
  3645. ).arg(_history->inboxReadTillId().bare
  3646. ).arg(Logs::b(_history->loadedAtBottom())
  3647. ).arg(showAtMsgId.bare));
  3648. auto from = _history;
  3649. auto offsetId = MsgId();
  3650. auto offset = 0;
  3651. auto loadCount = kMessagesPerPage;
  3652. if (_delayedShowAtMsgId == ShowAtUnreadMsgId) {
  3653. if (const auto around = _migrated ? _migrated->loadAroundId() : 0) {
  3654. from = _migrated;
  3655. offset = -loadCount / 2;
  3656. offsetId = around;
  3657. } else if (const auto around = _history->loadAroundId()) {
  3658. offset = -loadCount / 2;
  3659. offsetId = around;
  3660. } else {
  3661. loadCount = kMessagesPerPageFirst;
  3662. }
  3663. } else if (_delayedShowAtMsgId == ShowAtTheEndMsgId) {
  3664. loadCount = kMessagesPerPageFirst;
  3665. } else if (_delayedShowAtMsgId > 0) {
  3666. offset = -loadCount / 2;
  3667. offsetId = _delayedShowAtMsgId;
  3668. } else if (_delayedShowAtMsgId < 0 && _history->peer->isChannel()) {
  3669. if ((_delayedShowAtMsgId < 0)
  3670. && (-_delayedShowAtMsgId < ServerMaxMsgId)
  3671. && _migrated) {
  3672. from = _migrated;
  3673. offset = -loadCount / 2;
  3674. offsetId = -_delayedShowAtMsgId;
  3675. }
  3676. }
  3677. const auto offsetDate = 0;
  3678. const auto maxId = 0;
  3679. const auto minId = 0;
  3680. const auto historyHash = uint64(0);
  3681. const auto history = from;
  3682. const auto type = Data::Histories::RequestType::History;
  3683. auto &histories = history->owner().histories();
  3684. _delayedShowAtRequest = histories.sendRequest(history, type, [=](
  3685. Fn<void()> finish) {
  3686. return history->session().api().request(MTPmessages_GetHistory(
  3687. history->peer->input,
  3688. MTP_int(offsetId),
  3689. MTP_int(offsetDate),
  3690. MTP_int(offset),
  3691. MTP_int(loadCount),
  3692. MTP_int(maxId),
  3693. MTP_int(minId),
  3694. MTP_long(historyHash)
  3695. )).done([=](const MTPmessages_Messages &result) {
  3696. messagesReceived(history->peer, result, _delayedShowAtRequest);
  3697. finish();
  3698. }).fail([=](const MTP::Error &error) {
  3699. messagesFailed(error, _delayedShowAtRequest);
  3700. finish();
  3701. }).send();
  3702. });
  3703. }
  3704. void HistoryWidget::handleScroll() {
  3705. if (!_itemsRevealHeight) {
  3706. preloadHistoryIfNeeded();
  3707. }
  3708. visibleAreaUpdated();
  3709. if (!_itemsRevealHeight) {
  3710. updatePinnedViewer();
  3711. }
  3712. const auto now = crl::now();
  3713. if (!_synteticScrollEvent) {
  3714. _lastUserScrolled = now;
  3715. }
  3716. const auto scrollTop = _scroll->scrollTop();
  3717. if (scrollTop != _lastScrollTop) {
  3718. if (!_synteticScrollEvent) {
  3719. checkLastPinnedClickedIdReset(_lastScrollTop, scrollTop);
  3720. }
  3721. _lastScrolled = now;
  3722. _lastScrollTop = scrollTop;
  3723. }
  3724. }
  3725. bool HistoryWidget::isItemCompletelyHidden(HistoryItem *item) const {
  3726. const auto view = item ? item->mainView() : nullptr;
  3727. if (!view) {
  3728. return true;
  3729. }
  3730. const auto top = _list ? _list->itemTop(item) : -2;
  3731. if (top < 0) {
  3732. return true;
  3733. }
  3734. const auto bottom = top + view->height();
  3735. const auto scrollTop = _scroll->scrollTop();
  3736. const auto scrollBottom = scrollTop + _scroll->height();
  3737. return (top >= scrollBottom || bottom <= scrollTop);
  3738. }
  3739. void HistoryWidget::visibleAreaUpdated() {
  3740. if (_list && !_scroll->isHidden()) {
  3741. const auto scrollTop = _scroll->scrollTop();
  3742. const auto scrollBottom = scrollTop + _scroll->height();
  3743. _list->visibleAreaUpdated(scrollTop, scrollBottom);
  3744. controller()->floatPlayerAreaUpdated();
  3745. session().data().itemVisibilitiesUpdated();
  3746. }
  3747. }
  3748. void HistoryWidget::preloadHistoryIfNeeded() {
  3749. if (_firstLoadRequest
  3750. || _delayedShowAtRequest
  3751. || _scroll->isHidden()
  3752. || !_peer
  3753. || !_historyInited) {
  3754. return;
  3755. }
  3756. _cornerButtons.updateJumpDownVisibility();
  3757. _cornerButtons.updateUnreadThingsVisibility();
  3758. if (!_scrollToAnimation.animating()) {
  3759. preloadHistoryByScroll();
  3760. checkReplyReturns();
  3761. }
  3762. const auto hasNonEmpty = _history->findFirstNonEmpty();
  3763. const auto readyForBotStart = hasNonEmpty
  3764. || (_history->loadedAtTop() && _history->loadedAtBottom());
  3765. if (readyForBotStart && clearMaybeSendStart() && hasNonEmpty) {
  3766. sendBotStartCommand();
  3767. }
  3768. }
  3769. void HistoryWidget::preloadHistoryByScroll() {
  3770. if (_firstLoadRequest
  3771. || _delayedShowAtRequest
  3772. || _scroll->isHidden()
  3773. || !_peer
  3774. || !_historyInited) {
  3775. return;
  3776. }
  3777. auto scrollTop = _scroll->scrollTop();
  3778. auto scrollTopMax = _scroll->scrollTopMax();
  3779. auto scrollHeight = _scroll->height();
  3780. if (scrollTop + kPreloadHeightsCount * scrollHeight >= scrollTopMax) {
  3781. loadMessagesDown();
  3782. }
  3783. if (scrollTop <= kPreloadHeightsCount * scrollHeight) {
  3784. loadMessages();
  3785. }
  3786. if (session().supportMode()) {
  3787. crl::on_main(this, [=] { checkSupportPreload(); });
  3788. }
  3789. }
  3790. void HistoryWidget::checkSupportPreload(bool force) {
  3791. if (!_history
  3792. || _firstLoadRequest
  3793. || _preloadRequest
  3794. || _preloadDownRequest
  3795. || (_supportPreloadRequest && !force)
  3796. || controller()->activeChatEntryCurrent().key.history() != _history) {
  3797. return;
  3798. }
  3799. const auto setting = session().settings().supportSwitch();
  3800. const auto command = Support::GetSwitchCommand(setting);
  3801. const auto descriptor = !command
  3802. ? Dialogs::RowDescriptor()
  3803. : (*command == Shortcuts::Command::ChatNext)
  3804. ? controller()->resolveChatNext()
  3805. : controller()->resolveChatPrevious();
  3806. auto history = descriptor.key.history();
  3807. if (!history || _supportPreloadHistory == history) {
  3808. return;
  3809. }
  3810. clearSupportPreloadRequest();
  3811. _supportPreloadHistory = history;
  3812. _supportPreloadRequest = Support::SendPreloadRequest(history, [=] {
  3813. _supportPreloadRequest = 0;
  3814. _supportPreloadHistory = nullptr;
  3815. crl::on_main(this, [=] { checkSupportPreload(); });
  3816. });
  3817. }
  3818. void HistoryWidget::checkReplyReturns() {
  3819. if (_firstLoadRequest
  3820. || _scroll->isHidden()
  3821. || !_peer
  3822. || !_historyInited) {
  3823. return;
  3824. }
  3825. auto scrollTop = _scroll->scrollTop();
  3826. auto scrollTopMax = _scroll->scrollTopMax();
  3827. auto scrollHeight = _scroll->height();
  3828. while (const auto replyReturn = _cornerButtons.replyReturn()) {
  3829. auto below = !replyReturn->mainView()
  3830. && (replyReturn->history() == _history)
  3831. && !_history->isEmpty()
  3832. && (replyReturn->id
  3833. < _history->blocks.back()->messages.back()->data()->id);
  3834. if (!below) {
  3835. below = !replyReturn->mainView()
  3836. && (replyReturn->history() == _migrated)
  3837. && !_history->isEmpty();
  3838. }
  3839. if (!below) {
  3840. below = !replyReturn->mainView()
  3841. && _migrated
  3842. && (replyReturn->history() == _migrated)
  3843. && !_migrated->isEmpty()
  3844. && (replyReturn->id
  3845. < _migrated->blocks.back()->messages.back()->data()->id);
  3846. }
  3847. if (!below && replyReturn->mainView()) {
  3848. below = (scrollTop >= scrollTopMax)
  3849. || (_list->itemTop(replyReturn)
  3850. < scrollTop + scrollHeight / 2);
  3851. }
  3852. if (below) {
  3853. _cornerButtons.calculateNextReplyReturn();
  3854. } else {
  3855. break;
  3856. }
  3857. }
  3858. }
  3859. void HistoryWidget::cancelInlineBot() {
  3860. const auto &textWithTags = _field->getTextWithTags();
  3861. if (textWithTags.text.size() > _inlineBotUsername.size() + 2) {
  3862. setFieldText(
  3863. { '@' + _inlineBotUsername + ' ', TextWithTags::Tags() },
  3864. TextUpdateEvent::SaveDraft,
  3865. Ui::InputField::HistoryAction::NewEntry);
  3866. } else {
  3867. clearFieldText(
  3868. TextUpdateEvent::SaveDraft,
  3869. Ui::InputField::HistoryAction::NewEntry);
  3870. }
  3871. }
  3872. void HistoryWidget::windowIsVisibleChanged() {
  3873. InvokeQueued(this, [=] {
  3874. preloadHistoryIfNeeded();
  3875. });
  3876. }
  3877. TextWithEntities HistoryWidget::prepareTextForEditMsg() const {
  3878. const auto textWithTags = _field->getTextWithAppliedMarkdown();
  3879. const auto prepareFlags = Ui::ItemTextOptions(
  3880. _history,
  3881. session().user()).flags;
  3882. auto left = TextWithEntities {
  3883. textWithTags.text,
  3884. TextUtilities::ConvertTextTagsToEntities(textWithTags.tags) };
  3885. TextUtilities::PrepareForSending(left, prepareFlags);
  3886. return left;
  3887. }
  3888. void HistoryWidget::saveEditMsg() {
  3889. Expects(_history != nullptr);
  3890. if (_saveEditMsgRequestId) {
  3891. return;
  3892. }
  3893. const auto item = session().data().message(_history->peer, _editMsgId);
  3894. if (!item) {
  3895. cancelEdit();
  3896. return;
  3897. }
  3898. const auto webPageDraft = _preview->draft();
  3899. const auto sending = prepareTextForEditMsg();
  3900. const auto hasMediaWithCaption = item
  3901. && item->media()
  3902. && item->media()->allowsEditCaption();
  3903. if (sending.text.isEmpty()
  3904. && (webPageDraft.removed
  3905. || webPageDraft.url.isEmpty()
  3906. || !webPageDraft.manual)
  3907. && !hasMediaWithCaption) {
  3908. const auto suggestModerateActions = false;
  3909. controller()->show(
  3910. Box<DeleteMessagesBox>(item, suggestModerateActions));
  3911. return;
  3912. } else {
  3913. const auto maxCaptionSize = !hasMediaWithCaption
  3914. ? MaxMessageSize
  3915. : Data::PremiumLimits(&session()).captionLengthCurrent();
  3916. const auto remove = _fieldCharsCountManager.count() - maxCaptionSize;
  3917. if (remove > 0) {
  3918. controller()->showToast(
  3919. tr::lng_edit_limit_reached(tr::now, lt_count, remove));
  3920. #ifndef _DEBUG
  3921. return;
  3922. #else
  3923. if (!base::IsCtrlPressed()) {
  3924. return;
  3925. }
  3926. #endif
  3927. }
  3928. }
  3929. const auto weak = Ui::MakeWeak(this);
  3930. const auto history = _history;
  3931. const auto done = [=](mtpRequestId requestId) {
  3932. crl::guard(weak, [=] {
  3933. if (requestId == _saveEditMsgRequestId) {
  3934. _saveEditMsgRequestId = 0;
  3935. cancelEdit();
  3936. }
  3937. })();
  3938. if (const auto editDraft = history->localEditDraft({})) {
  3939. if (editDraft->saveRequestId == requestId) {
  3940. history->clearLocalEditDraft({});
  3941. history->session().local().writeDrafts(history);
  3942. }
  3943. }
  3944. };
  3945. const auto fail = [=](const QString &error, mtpRequestId requestId) {
  3946. if (const auto editDraft = history->localEditDraft({})) {
  3947. if (editDraft->saveRequestId == requestId) {
  3948. editDraft->saveRequestId = 0;
  3949. }
  3950. }
  3951. crl::guard(weak, [=] {
  3952. if (requestId == _saveEditMsgRequestId) {
  3953. _saveEditMsgRequestId = 0;
  3954. }
  3955. if (ranges::contains(Api::kDefaultEditMessagesErrors, error)) {
  3956. controller()->showToast(tr::lng_edit_error(tr::now));
  3957. } else if (error == u"MESSAGE_NOT_MODIFIED"_q) {
  3958. cancelEdit();
  3959. } else if (error == u"MESSAGE_EMPTY"_q) {
  3960. _field->selectAll();
  3961. setInnerFocus();
  3962. } else {
  3963. controller()->showToast(tr::lng_edit_error(tr::now));
  3964. }
  3965. update();
  3966. })();
  3967. };
  3968. _saveEditMsgRequestId = Api::EditTextMessage(
  3969. item,
  3970. sending,
  3971. webPageDraft,
  3972. { .invertCaption = _mediaEditManager.invertCaption() },
  3973. done,
  3974. fail,
  3975. _mediaEditManager.spoilered());
  3976. }
  3977. void HistoryWidget::hideChildWidgets() {
  3978. if (Ui::InFocusChain(this)) {
  3979. // Removing focus from list clears selected and updates top bar.
  3980. setFocus();
  3981. }
  3982. if (_tabbedPanel) {
  3983. _tabbedPanel->hideFast();
  3984. }
  3985. if (_pinnedBar) {
  3986. _pinnedBar->hide();
  3987. }
  3988. if (_sponsoredMessageBar) {
  3989. _sponsoredMessageBar->toggle(false, anim::type::instant);
  3990. }
  3991. if (_translateBar) {
  3992. _translateBar->hide();
  3993. }
  3994. if (_groupCallBar) {
  3995. _groupCallBar->hide();
  3996. }
  3997. if (_requestsBar) {
  3998. _requestsBar->hide();
  3999. }
  4000. if (_voiceRecordBar) {
  4001. _voiceRecordBar->hideFast();
  4002. }
  4003. if (_composeSearch) {
  4004. _composeSearch->hideAnimated();
  4005. }
  4006. if (_chooseTheme) {
  4007. _chooseTheme->hide();
  4008. }
  4009. if (_paysStatus) {
  4010. _paysStatus->hide();
  4011. }
  4012. if (_contactStatus) {
  4013. _contactStatus->hide();
  4014. }
  4015. if (_businessBotStatus) {
  4016. _businessBotStatus->hide();
  4017. }
  4018. hideChildren();
  4019. }
  4020. void HistoryWidget::hideSelectorControlsAnimated() {
  4021. if (_autocomplete) {
  4022. _autocomplete->hideAnimated();
  4023. }
  4024. if (_supportAutocomplete) {
  4025. _supportAutocomplete->hide();
  4026. }
  4027. if (_tabbedPanel) {
  4028. _tabbedPanel->hideAnimated();
  4029. }
  4030. if (_inlineResults) {
  4031. _inlineResults->hideAnimated();
  4032. }
  4033. }
  4034. Api::SendAction HistoryWidget::prepareSendAction(
  4035. Api::SendOptions options) const {
  4036. auto result = Api::SendAction(_history, options);
  4037. result.replyTo = replyTo();
  4038. result.options.sendAs = _sendAs
  4039. ? _history->session().sendAsPeers().resolveChosen(
  4040. _history->peer).get()
  4041. : nullptr;
  4042. return result;
  4043. }
  4044. void HistoryWidget::sendVoice(const VoiceToSend &data) {
  4045. if (!canWriteMessage() || data.bytes.isEmpty() || !_history) {
  4046. return;
  4047. }
  4048. const auto withPaymentApproved = [=](int approved) {
  4049. auto copy = data;
  4050. copy.options.starsApproved = approved;
  4051. sendVoice(copy);
  4052. };
  4053. const auto checked = checkSendPayment(
  4054. 1 + int(_forwardPanel->items().size()),
  4055. data.options.starsApproved,
  4056. withPaymentApproved);
  4057. if (!checked) {
  4058. return;
  4059. }
  4060. auto action = prepareSendAction(data.options);
  4061. session().api().sendVoiceMessage(
  4062. data.bytes,
  4063. data.waveform,
  4064. data.duration,
  4065. data.video,
  4066. action);
  4067. _voiceRecordBar->clearListenState();
  4068. }
  4069. void HistoryWidget::send(Api::SendOptions options) {
  4070. if (!_history) {
  4071. return;
  4072. } else if (_editMsgId) {
  4073. saveEditMsg();
  4074. return;
  4075. } else if (!options.scheduled && showSlowmodeError()) {
  4076. return;
  4077. } else if (_voiceRecordBar->isListenState()) {
  4078. _voiceRecordBar->requestToSendWithOptions(options);
  4079. return;
  4080. }
  4081. if (!options.scheduled) {
  4082. _cornerButtons.clearReplyReturns();
  4083. }
  4084. auto message = Api::MessageToSend(prepareSendAction(options));
  4085. message.textWithTags = _field->getTextWithAppliedMarkdown();
  4086. message.webPage = _preview->draft();
  4087. const auto ignoreSlowmodeCountdown = (options.scheduled != 0);
  4088. const auto withPaymentApproved = [=](int approved) {
  4089. auto copy = options;
  4090. copy.starsApproved = approved;
  4091. send(copy);
  4092. };
  4093. if (showSendMessageError(
  4094. message.textWithTags,
  4095. ignoreSlowmodeCountdown,
  4096. withPaymentApproved,
  4097. options.starsApproved)) {
  4098. return;
  4099. }
  4100. // Just a flag not to drop reply info if we're not sending anything.
  4101. _justMarkingAsRead = !HasSendText(_field)
  4102. && message.webPage.url.isEmpty();
  4103. session().api().sendMessage(std::move(message));
  4104. _justMarkingAsRead = false;
  4105. clearFieldText();
  4106. if (_preview) {
  4107. _preview->apply({ .removed = true });
  4108. }
  4109. _saveDraftText = true;
  4110. _saveDraftStart = crl::now();
  4111. saveDraft();
  4112. hideSelectorControlsAnimated();
  4113. setInnerFocus();
  4114. if (!_keyboard->hasMarkup() && _keyboard->forceReply() && !_kbReplyTo) {
  4115. toggleKeyboard();
  4116. }
  4117. session().changes().historyUpdated(
  4118. _history,
  4119. (options.scheduled
  4120. ? Data::HistoryUpdate::Flag::ScheduledSent
  4121. : Data::HistoryUpdate::Flag::MessageSent));
  4122. }
  4123. void HistoryWidget::sendWithModifiers(Qt::KeyboardModifiers modifiers) {
  4124. send({ .handleSupportSwitch = Support::HandleSwitch(modifiers) });
  4125. }
  4126. void HistoryWidget::sendScheduled(Api::SendOptions initialOptions) {
  4127. if (!_list) {
  4128. return;
  4129. }
  4130. const auto ignoreSlowmodeCountdown = true;
  4131. if (showSendMessageError(
  4132. _field->getTextWithAppliedMarkdown(),
  4133. ignoreSlowmodeCountdown)) {
  4134. return;
  4135. }
  4136. controller()->show(
  4137. HistoryView::PrepareScheduleBox(
  4138. _list,
  4139. controller()->uiShow(),
  4140. sendButtonDefaultDetails(),
  4141. [=](Api::SendOptions options) { send(options); },
  4142. initialOptions));
  4143. }
  4144. SendMenu::Details HistoryWidget::sendMenuDetails() const {
  4145. const auto type = !_peer
  4146. ? SendMenu::Type::Disabled
  4147. : _peer->starsPerMessageChecked()
  4148. ? SendMenu::Type::SilentOnly
  4149. : _peer->isSelf()
  4150. ? SendMenu::Type::Reminder
  4151. : HistoryView::CanScheduleUntilOnline(_peer)
  4152. ? SendMenu::Type::ScheduledToUser
  4153. : SendMenu::Type::Scheduled;
  4154. const auto effectAllowed = _peer && _peer->isUser();
  4155. return { .type = type, .effectAllowed = effectAllowed };
  4156. }
  4157. SendMenu::Details HistoryWidget::saveMenuDetails() const {
  4158. return (_editMsgId && _replyEditMsg)
  4159. ? _mediaEditManager.sendMenuDetails(HasSendText(_field))
  4160. : SendMenu::Details();
  4161. }
  4162. auto HistoryWidget::computeSendButtonType() const {
  4163. using Type = Ui::SendButton::Type;
  4164. if (_editMsgId) {
  4165. return Type::Save;
  4166. } else if (_isInlineBot) {
  4167. return Type::Cancel;
  4168. } else if (showRecordButton()) {
  4169. const auto both = Webrtc::RecordAvailability::VideoAndAudio;
  4170. const auto video = Core::App().settings().recordVideoMessages();
  4171. return (video && _recordAvailability == both)
  4172. ? Type::Round
  4173. : Type::Record;
  4174. }
  4175. return Type::Send;
  4176. }
  4177. SendMenu::Details HistoryWidget::sendButtonMenuDetails() const {
  4178. using Type = Ui::SendButton::Type;
  4179. const auto type = computeSendButtonType();
  4180. if (type == Type::Save) {
  4181. return saveMenuDetails();
  4182. } else if (type != Type::Send) {
  4183. return {};
  4184. }
  4185. return sendButtonDefaultDetails();
  4186. }
  4187. SendMenu::Details HistoryWidget::sendButtonDefaultDetails() const {
  4188. auto result = sendMenuDetails();
  4189. if (!HasSendText(_field) && !_previewDrawPreview) {
  4190. result.effectAllowed = false;
  4191. }
  4192. return result;
  4193. }
  4194. void HistoryWidget::unblockUser() {
  4195. if (const auto user = _peer ? _peer->asUser() : nullptr) {
  4196. const auto show = controller()->uiShow();
  4197. Window::PeerMenuUnblockUserWithBotRestart(show, user);
  4198. } else {
  4199. updateControlsVisibility();
  4200. }
  4201. }
  4202. void HistoryWidget::sendBotStartCommand() {
  4203. if (!_peer
  4204. || !_peer->isUser()
  4205. || !_peer->asUser()->isBot()
  4206. || !_canSendMessages) {
  4207. updateControlsVisibility();
  4208. return;
  4209. }
  4210. session().api().sendBotStart(controller()->uiShow(), _peer->asUser());
  4211. updateControlsVisibility();
  4212. updateControlsGeometry();
  4213. }
  4214. void HistoryWidget::joinChannel() {
  4215. if (!_peer || !_peer->isChannel() || !isJoinChannel()) {
  4216. updateControlsVisibility();
  4217. return;
  4218. }
  4219. session().api().joinChannel(_peer->asChannel());
  4220. }
  4221. void HistoryWidget::toggleMuteUnmute() {
  4222. const auto wasMuted = _history->muted();
  4223. const auto muteForSeconds = Data::MuteValue{
  4224. .unmute = wasMuted,
  4225. .forever = !wasMuted,
  4226. };
  4227. session().data().notifySettings().update(_peer, muteForSeconds);
  4228. }
  4229. void HistoryWidget::reportSelectedMessages() {
  4230. if (!_list || !_chooseForReport || !_list->getSelectionState().count) {
  4231. return;
  4232. }
  4233. const auto ids = _list->getSelectedItems();
  4234. const auto done = _chooseForReport->callback;
  4235. clearSelected();
  4236. controller()->clearChooseReportMessages();
  4237. if (done) {
  4238. done(ranges::views::all(
  4239. ids
  4240. ) | ranges::views::transform(&FullMsgId::msg) | ranges::to_vector);
  4241. }
  4242. }
  4243. History *HistoryWidget::history() const {
  4244. return _history;
  4245. }
  4246. PeerData *HistoryWidget::peer() const {
  4247. return _peer;
  4248. }
  4249. // Sometimes _showAtMsgId is set directly.
  4250. void HistoryWidget::setMsgId(
  4251. MsgId showAtMsgId,
  4252. const Window::SectionShow &params) {
  4253. _showAtMsgParams = params;
  4254. if (_showAtMsgId != showAtMsgId) {
  4255. _showAtMsgId = showAtMsgId;
  4256. if (_history) {
  4257. controller()->setActiveChatEntry({
  4258. _history,
  4259. FullMsgId(_history->peer->id, _showAtMsgId) });
  4260. }
  4261. }
  4262. }
  4263. MsgId HistoryWidget::msgId() const {
  4264. return _showAtMsgId;
  4265. }
  4266. void HistoryWidget::showAnimated(
  4267. Window::SlideDirection direction,
  4268. const Window::SectionSlideParams &params) {
  4269. _showAnimation = nullptr;
  4270. // If we show pinned bar here, we don't want it to change the
  4271. // calculated and prepared scrollTop of the messages history.
  4272. _preserveScrollTop = true;
  4273. show();
  4274. _topBar->finishAnimating();
  4275. _cornerButtons.finishAnimations();
  4276. if (_pinnedBar) {
  4277. _pinnedBar->finishAnimating();
  4278. }
  4279. if (_translateBar) {
  4280. _translateBar->finishAnimating();
  4281. }
  4282. if (_groupCallBar) {
  4283. _groupCallBar->finishAnimating();
  4284. }
  4285. if (_requestsBar) {
  4286. _requestsBar->finishAnimating();
  4287. }
  4288. _topShadow->setVisible(params.withTopBarShadow ? false : true);
  4289. _preserveScrollTop = false;
  4290. _stickerToast = nullptr;
  4291. auto newContentCache = Ui::GrabWidget(this);
  4292. hideChildWidgets();
  4293. if (params.withTopBarShadow) _topShadow->show();
  4294. if (_history) {
  4295. _topBar->show();
  4296. _topBar->setAnimatingMode(true);
  4297. }
  4298. _showAnimation = std::make_unique<Window::SlideAnimation>();
  4299. _showAnimation->setDirection(direction);
  4300. _showAnimation->setRepaintCallback([=] { update(); });
  4301. _showAnimation->setFinishedCallback([=] { showFinished(); });
  4302. _showAnimation->setPixmaps(params.oldContentCache, newContentCache);
  4303. _showAnimation->start();
  4304. activate();
  4305. }
  4306. void HistoryWidget::showFinished() {
  4307. _cornerButtons.finishAnimations();
  4308. if (_pinnedBar) {
  4309. _pinnedBar->finishAnimating();
  4310. }
  4311. if (_translateBar) {
  4312. _translateBar->finishAnimating();
  4313. }
  4314. if (_groupCallBar) {
  4315. _groupCallBar->finishAnimating();
  4316. }
  4317. if (_requestsBar) {
  4318. _requestsBar->finishAnimating();
  4319. }
  4320. _showAnimation = nullptr;
  4321. doneShow();
  4322. synteticScrollToY(_scroll->scrollTop());
  4323. }
  4324. void HistoryWidget::doneShow() {
  4325. _topBar->setAnimatingMode(false);
  4326. updateCanSendMessage();
  4327. updateBotKeyboard();
  4328. updateControlsVisibility();
  4329. if (!_historyInited) {
  4330. updateHistoryGeometry(true);
  4331. } else {
  4332. handlePendingHistoryUpdate();
  4333. }
  4334. // If we show pinned bar here, we don't want it to change the
  4335. // calculated and prepared scrollTop of the messages history.
  4336. _preserveScrollTop = true;
  4337. preloadHistoryIfNeeded();
  4338. updatePinnedViewer();
  4339. if (_pinnedBar) {
  4340. _pinnedBar->finishAnimating();
  4341. }
  4342. checkSponsoredMessageBar();
  4343. if (_sponsoredMessageBar) {
  4344. _sponsoredMessageBar->finishAnimating();
  4345. }
  4346. if (_translateBar) {
  4347. _translateBar->finishAnimating();
  4348. }
  4349. if (_groupCallBar) {
  4350. _groupCallBar->finishAnimating();
  4351. }
  4352. if (_requestsBar) {
  4353. _requestsBar->finishAnimating();
  4354. }
  4355. checkActivation();
  4356. controller()->widget()->setInnerFocus();
  4357. _preserveScrollTop = false;
  4358. checkSuggestToGigagroup();
  4359. }
  4360. void HistoryWidget::cornerButtonsShowAtPosition(
  4361. Data::MessagePosition position) {
  4362. if (position == Data::UnreadMessagePosition) {
  4363. DEBUG_LOG(("JumpToEnd(%1, %2, %3): Show at unread requested."
  4364. ).arg(_history->peer->name()
  4365. ).arg(_history->inboxReadTillId().bare
  4366. ).arg(Logs::b(_history->loadedAtBottom())));
  4367. showHistory(_peer->id, ShowAtUnreadMsgId);
  4368. } else if (_peer && position.fullId.peer == _peer->id) {
  4369. showHistory(_peer->id, position.fullId.msg);
  4370. } else if (_migrated && position.fullId.peer == _migrated->peer->id) {
  4371. showHistory(_peer->id, -position.fullId.msg);
  4372. }
  4373. }
  4374. Data::Thread *HistoryWidget::cornerButtonsThread() {
  4375. return _history;
  4376. }
  4377. FullMsgId HistoryWidget::cornerButtonsCurrentId() {
  4378. return (_migrated && _showAtMsgId < 0)
  4379. ? FullMsgId(_migrated->peer->id, -_showAtMsgId)
  4380. : (_history && _showAtMsgId > 0)
  4381. ? FullMsgId(_history->peer->id, _showAtMsgId)
  4382. : FullMsgId();
  4383. }
  4384. bool HistoryWidget::checkSendPayment(
  4385. int messagesCount,
  4386. int starsApproved,
  4387. Fn<void(int)> withPaymentApproved) {
  4388. return _peer
  4389. && _sendPayment.check(
  4390. controller(),
  4391. _peer,
  4392. messagesCount,
  4393. starsApproved,
  4394. std::move(withPaymentApproved));
  4395. }
  4396. void HistoryWidget::checkSuggestToGigagroup() {
  4397. const auto group = _peer ? _peer->asMegagroup() : nullptr;
  4398. if (!group || !group->owner().suggestToGigagroup(group)) {
  4399. return;
  4400. }
  4401. InvokeQueued(_list, [=] {
  4402. if (!controller()->isLayerShown()) {
  4403. group->owner().setSuggestToGigagroup(group, false);
  4404. group->session().api().request(MTPhelp_DismissSuggestion(
  4405. group->input,
  4406. MTP_string("convert_to_gigagroup")
  4407. )).send();
  4408. controller()->show(Box([=](not_null<Ui::GenericBox*> box) {
  4409. box->setTitle(tr::lng_gigagroup_suggest_title());
  4410. box->addRow(
  4411. object_ptr<Ui::FlatLabel>(
  4412. box,
  4413. tr::lng_gigagroup_suggest_text(
  4414. ) | Ui::Text::ToRichLangValue(),
  4415. st::infoAboutGigagroup));
  4416. box->addButton(
  4417. tr::lng_gigagroup_suggest_more(),
  4418. AboutGigagroupCallback(group, controller()));
  4419. box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
  4420. }));
  4421. }
  4422. });
  4423. }
  4424. void HistoryWidget::finishAnimating() {
  4425. if (!_showAnimation) {
  4426. return;
  4427. }
  4428. _showAnimation = nullptr;
  4429. _topShadow->setVisible(_peer != nullptr);
  4430. _topBar->setVisible(_peer != nullptr);
  4431. _cornerButtons.finishAnimations();
  4432. }
  4433. void HistoryWidget::chooseAttach(
  4434. std::optional<bool> overrideSendImagesAsPhotos) {
  4435. if (_editMsgId) {
  4436. controller()->showToast(tr::lng_edit_caption_attach(tr::now));
  4437. return;
  4438. }
  4439. if (!_peer || !_canSendMessages) {
  4440. return;
  4441. } else if (const auto error = Data::AnyFileRestrictionError(_peer)) {
  4442. Data::ShowSendErrorToast(controller(), _peer, error);
  4443. return;
  4444. } else if (showSlowmodeError()) {
  4445. return;
  4446. }
  4447. const auto filter = (overrideSendImagesAsPhotos == true)
  4448. ? FileDialog::PhotoVideoFilesFilter()
  4449. : FileDialog::AllOrImagesFilter();
  4450. const auto callbackOnResult = crl::guard(this, [=](
  4451. FileDialog::OpenResult &&result) {
  4452. if (result.paths.isEmpty() && result.remoteContent.isEmpty()) {
  4453. return;
  4454. }
  4455. if (!result.remoteContent.isEmpty()) {
  4456. auto read = Images::Read({
  4457. .content = result.remoteContent,
  4458. });
  4459. if (!read.image.isNull() && !read.animated) {
  4460. confirmSendingFiles(
  4461. std::move(read.image),
  4462. std::move(result.remoteContent),
  4463. overrideSendImagesAsPhotos);
  4464. } else {
  4465. uploadFile(result.remoteContent, SendMediaType::File);
  4466. }
  4467. } else {
  4468. const auto premium = controller()->session().user()->isPremium();
  4469. auto list = Storage::PrepareMediaList(
  4470. result.paths,
  4471. st::sendMediaPreviewSize,
  4472. premium);
  4473. list.overrideSendImagesAsPhotos = overrideSendImagesAsPhotos;
  4474. confirmSendingFiles(std::move(list));
  4475. }
  4476. });
  4477. FileDialog::GetOpenPaths(
  4478. this,
  4479. tr::lng_choose_files(tr::now),
  4480. filter,
  4481. callbackOnResult,
  4482. nullptr);
  4483. }
  4484. void HistoryWidget::sendButtonClicked() {
  4485. const auto type = _send->type();
  4486. if (type == Ui::SendButton::Type::Cancel) {
  4487. cancelInlineBot();
  4488. } else if (type != Ui::SendButton::Type::Record
  4489. && type != Ui::SendButton::Type::Round) {
  4490. send({});
  4491. }
  4492. }
  4493. void HistoryWidget::leaveEventHook(QEvent *e) {
  4494. if (hasMouseTracking()) {
  4495. mouseMoveEvent(nullptr);
  4496. }
  4497. }
  4498. void HistoryWidget::mouseMoveEvent(QMouseEvent *e) {
  4499. const auto pos = e ? e->pos() : mapFromGlobal(QCursor::pos());
  4500. updateOverStates(pos);
  4501. }
  4502. void HistoryWidget::updateOverStates(QPoint pos) {
  4503. const auto isReadyToForward = readyToForward();
  4504. const auto detailsRect = QRect(
  4505. 0,
  4506. _field->y() - st::historySendPadding - st::historyReplyHeight,
  4507. width() - _fieldBarCancel->width(),
  4508. st::historyReplyHeight);
  4509. const auto hasWebPage = !!_previewDrawPreview;
  4510. const auto inDetails = detailsRect.contains(pos)
  4511. && (_editMsgId || replyTo() || isReadyToForward || hasWebPage);
  4512. const auto inPhotoEdit = inDetails
  4513. && _photoEditMedia
  4514. && QRect(
  4515. detailsRect.x() + st::historyReplySkip,
  4516. (detailsRect.y()
  4517. + (detailsRect.height() - st::historyReplyPreview) / 2),
  4518. st::historyReplyPreview,
  4519. st::historyReplyPreview).contains(pos);
  4520. const auto inClickable = inDetails;
  4521. if (_inPhotoEdit != inPhotoEdit) {
  4522. _inPhotoEdit = inPhotoEdit;
  4523. if (_photoEditMedia) {
  4524. _inPhotoEditOver.start(
  4525. [=] { updateField(); },
  4526. _inPhotoEdit ? 0. : 1.,
  4527. _inPhotoEdit ? 1. : 0.,
  4528. st::defaultMessageBar.duration);
  4529. } else {
  4530. _inPhotoEditOver.stop();
  4531. }
  4532. }
  4533. _inDetails = inDetails && !inPhotoEdit;
  4534. if (inClickable != _inClickable) {
  4535. _inClickable = inClickable;
  4536. setCursor(_inClickable ? style::cur_pointer : style::cur_default);
  4537. }
  4538. }
  4539. void HistoryWidget::leaveToChildEvent(QEvent *e, QWidget *child) {
  4540. // e -- from enterEvent() of child TWidget
  4541. if (hasMouseTracking()) {
  4542. updateOverStates(mapFromGlobal(QCursor::pos()));
  4543. }
  4544. }
  4545. void HistoryWidget::mouseReleaseEvent(QMouseEvent *e) {
  4546. }
  4547. void HistoryWidget::sendBotCommand(const Bot::SendCommandRequest &request) {
  4548. sendBotCommand(request, {});
  4549. }
  4550. void HistoryWidget::sendBotCommand(
  4551. const Bot::SendCommandRequest &request,
  4552. Api::SendOptions options) {
  4553. // replyTo != 0 from ReplyKeyboardMarkup, == 0 from command links
  4554. if (_peer != request.peer.get()) {
  4555. return;
  4556. } else if (showSlowmodeError()) {
  4557. return;
  4558. }
  4559. const auto withPaymentApproved = [=](int approved) {
  4560. auto copy = options;
  4561. copy.starsApproved = approved;
  4562. sendBotCommand(request, copy);
  4563. };
  4564. const auto checked = checkSendPayment(
  4565. 1,
  4566. options.starsApproved,
  4567. withPaymentApproved);
  4568. if (!checked) {
  4569. return;
  4570. }
  4571. const auto forMsgId = _keyboard->forMsgId();
  4572. const auto lastKeyboardUsed = (forMsgId == request.replyTo.messageId)
  4573. && (forMsgId == FullMsgId(_peer->id, _history->lastKeyboardId));
  4574. // 'bot' may be nullptr in case of sending from FieldAutocomplete.
  4575. const auto toSend = (request.replyTo/* || !bot*/)
  4576. ? request.command
  4577. : Bot::WrapCommandInChat(_peer, request.command, request.context);
  4578. auto message = Api::MessageToSend(prepareSendAction(options));
  4579. message.textWithTags = { toSend, TextWithTags::Tags() };
  4580. message.action.replyTo = request.replyTo
  4581. ? ((!_peer->isUser()/* && (botStatus == 0 || botStatus == 2)*/)
  4582. ? request.replyTo
  4583. : replyTo())
  4584. : FullReplyTo();
  4585. session().api().sendMessage(std::move(message));
  4586. if (request.replyTo) {
  4587. if (_replyTo == request.replyTo) {
  4588. cancelReply();
  4589. saveCloudDraft();
  4590. }
  4591. if (_keyboard->singleUse()
  4592. && _keyboard->hasMarkup()
  4593. && lastKeyboardUsed) {
  4594. if (_kbShown) {
  4595. toggleKeyboard(false);
  4596. }
  4597. _history->lastKeyboardUsed = true;
  4598. }
  4599. }
  4600. setInnerFocus();
  4601. }
  4602. void HistoryWidget::hideSingleUseKeyboard(FullMsgId replyToId) {
  4603. if (!_peer || _peer->id != replyToId.peer) {
  4604. return;
  4605. }
  4606. const auto lastKeyboardUsed = (_keyboard->forMsgId() == replyToId)
  4607. && (_keyboard->forMsgId()
  4608. == FullMsgId(_peer->id, _history->lastKeyboardId));
  4609. if (replyToId) {
  4610. if (_replyTo.messageId == replyToId) {
  4611. cancelReply();
  4612. saveCloudDraft();
  4613. }
  4614. if (_keyboard->singleUse()
  4615. && _keyboard->hasMarkup()
  4616. && lastKeyboardUsed) {
  4617. if (_kbShown) {
  4618. toggleKeyboard(false);
  4619. }
  4620. _history->lastKeyboardUsed = true;
  4621. }
  4622. }
  4623. }
  4624. bool HistoryWidget::insertBotCommand(const QString &cmd) {
  4625. if (!_canSendTexts) {
  4626. return false;
  4627. }
  4628. const auto insertingInlineBot = !cmd.isEmpty() && (cmd.at(0) == '@');
  4629. auto toInsert = cmd;
  4630. if (!toInsert.isEmpty() && !insertingInlineBot) {
  4631. auto bot = (PeerData*)(_peer->asUser());
  4632. if (!bot) {
  4633. if (const auto link = HistoryView::Element::HoveredLink()) {
  4634. bot = link->data()->fromOriginal().get();
  4635. }
  4636. }
  4637. if (bot && (!bot->isUser() || !bot->asUser()->isBot())) {
  4638. bot = nullptr;
  4639. }
  4640. const auto username = bot ? bot->asUser()->username() : QString();
  4641. const auto botStatus = _peer->isChat()
  4642. ? _peer->asChat()->botStatus
  4643. : _peer->isMegagroup()
  4644. ? _peer->asChannel()->mgInfo->botStatus
  4645. : -1;
  4646. if ((toInsert.indexOf('@') < 0)
  4647. && !username.isEmpty()
  4648. && (botStatus == 0 || botStatus == 2)) {
  4649. toInsert += '@' + username;
  4650. }
  4651. }
  4652. toInsert += ' ';
  4653. if (!insertingInlineBot) {
  4654. auto &textWithTags = _field->getTextWithTags();
  4655. auto textWithTagsToSet = TextWithTags();
  4656. const auto m = QRegularExpression(
  4657. u"^/[A-Za-z_0-9]{0,64}(@[A-Za-z_0-9]{0,32})?(\\s|$)"_q).match(
  4658. textWithTags.text);
  4659. textWithTagsToSet = m.hasMatch()
  4660. ? _field->getTextWithTagsPart(m.capturedLength())
  4661. : textWithTags;
  4662. textWithTagsToSet.text = toInsert + textWithTagsToSet.text;
  4663. for (auto &tag : textWithTagsToSet.tags) {
  4664. tag.offset += toInsert.size();
  4665. }
  4666. _field->setTextWithTags(textWithTagsToSet);
  4667. auto cur = QTextCursor(_field->textCursor());
  4668. cur.movePosition(QTextCursor::End);
  4669. _field->setTextCursor(cur);
  4670. } else {
  4671. setFieldText(
  4672. { toInsert, TextWithTags::Tags() },
  4673. TextUpdateEvent::SaveDraft,
  4674. Ui::InputField::HistoryAction::NewEntry);
  4675. setInnerFocus();
  4676. return true;
  4677. }
  4678. return false;
  4679. }
  4680. bool HistoryWidget::eventFilter(QObject *obj, QEvent *e) {
  4681. if (e->type() == QEvent::KeyPress) {
  4682. const auto k = static_cast<QKeyEvent*>(e);
  4683. if ((k->modifiers() & kCommonModifiers) == Qt::ControlModifier) {
  4684. if (k->key() == Qt::Key_Up) {
  4685. #ifdef Q_OS_MAC
  4686. // Cmd + Up is used instead of Home.
  4687. if (HasSendText(_field)) {
  4688. return false;
  4689. }
  4690. #endif
  4691. return replyToPreviousMessage();
  4692. } else if (k->key() == Qt::Key_Down) {
  4693. #ifdef Q_OS_MAC
  4694. // Cmd + Down is used instead of End.
  4695. if (HasSendText(_field)) {
  4696. return false;
  4697. }
  4698. #endif
  4699. return replyToNextMessage();
  4700. }
  4701. }
  4702. }
  4703. return TWidget::eventFilter(obj, e);
  4704. }
  4705. bool HistoryWidget::floatPlayerHandleWheelEvent(QEvent *e) {
  4706. return _peer ? _scroll->viewportEvent(e) : false;
  4707. }
  4708. QRect HistoryWidget::floatPlayerAvailableRect() {
  4709. return _peer ? mapToGlobal(_scroll->geometry()) : mapToGlobal(rect());
  4710. }
  4711. bool HistoryWidget::readyToForward() const {
  4712. return _canSendMessages && !_forwardPanel->empty();
  4713. }
  4714. bool HistoryWidget::hasSilentToggle() const {
  4715. return _peer
  4716. && _peer->isBroadcast()
  4717. && Data::CanSendAnything(_peer)
  4718. && !session().data().notifySettings().silentPostsUnknown(_peer);
  4719. }
  4720. void HistoryWidget::handleSupportSwitch(not_null<History*> updated) {
  4721. if (_history != updated || !session().supportMode()) {
  4722. return;
  4723. }
  4724. const auto setting = session().settings().supportSwitch();
  4725. if (auto method = Support::GetSwitchMethod(setting)) {
  4726. crl::on_main(this, std::move(method));
  4727. }
  4728. }
  4729. bool HistoryWidget::isBotStart() const {
  4730. const auto user = _peer ? _peer->asUser() : nullptr;
  4731. if (!user
  4732. || !user->isBot()
  4733. || !_canSendMessages) {
  4734. return false;
  4735. } else if (!user->botInfo->startToken.isEmpty()) {
  4736. return true;
  4737. } else if (_history->isEmpty() && !_history->lastMessage()) {
  4738. return true;
  4739. }
  4740. return false;
  4741. }
  4742. bool HistoryWidget::isReportMessages() const {
  4743. return _peer && _chooseForReport && _chooseForReport->active;
  4744. }
  4745. bool HistoryWidget::isBlocked() const {
  4746. return _peer && _peer->isUser() && _peer->asUser()->isBlocked();
  4747. }
  4748. bool HistoryWidget::isJoinChannel() const {
  4749. return _peer && _peer->isChannel() && !_peer->asChannel()->amIn();
  4750. }
  4751. bool HistoryWidget::isChoosingTheme() const {
  4752. return _chooseTheme && _chooseTheme->shouldBeShown();
  4753. }
  4754. bool HistoryWidget::isMuteUnmute() const {
  4755. return _peer
  4756. && ((_peer->isBroadcast() && !_peer->asChannel()->canPostMessages())
  4757. || (_peer->isGigagroup() && !Data::CanSendAnything(_peer))
  4758. || _peer->isRepliesChat()
  4759. || _peer->isVerifyCodes());
  4760. }
  4761. bool HistoryWidget::isSearching() const {
  4762. return _composeSearch != nullptr;
  4763. }
  4764. bool HistoryWidget::showRecordButton() const {
  4765. return (_recordAvailability != Webrtc::RecordAvailability::None)
  4766. && !_voiceRecordBar->isListenState()
  4767. && !_voiceRecordBar->isRecordingByAnotherBar()
  4768. && !HasSendText(_field)
  4769. && !_previewDrawPreview
  4770. && (_replyTo || !readyToForward())
  4771. && !_editMsgId;
  4772. }
  4773. bool HistoryWidget::showInlineBotCancel() const {
  4774. return _inlineBot && !_inlineLookingUpBot;
  4775. }
  4776. void HistoryWidget::updateSendButtonType() {
  4777. using Type = Ui::SendButton::Type;
  4778. const auto type = computeSendButtonType();
  4779. // This logic is duplicated in RepliesWidget.
  4780. const auto disabledBySlowmode = _peer
  4781. && _peer->slowmodeApplied()
  4782. && (_history->latestSendingMessage() != nullptr);
  4783. const auto delay = [&] {
  4784. return (type != Type::Cancel && type != Type::Save && _peer)
  4785. ? _peer->slowmodeSecondsLeft()
  4786. : 0;
  4787. }();
  4788. const auto perMessage = _peer ? _peer->starsPerMessageChecked() : 0;
  4789. const auto messages = !_peer
  4790. ? 0
  4791. : _voiceRecordBar->isListenState()
  4792. ? 1
  4793. : ComputeSendingMessagesCount(_history, {
  4794. .forward = &_forwardPanel->items(),
  4795. .text = &_field->getTextWithTags(),
  4796. });
  4797. const auto stars = perMessage ? (perMessage * messages) : 0;
  4798. _send->setState({
  4799. .type = (delay > 0) ? Type::Slowmode : type,
  4800. .slowmodeDelay = delay,
  4801. .starsToSend = stars,
  4802. });
  4803. _send->setDisabled(disabledBySlowmode
  4804. && (type == Type::Send
  4805. || type == Type::Record
  4806. || type == Type::Round));
  4807. if (delay != 0) {
  4808. base::call_delayed(
  4809. kRefreshSlowmodeLabelTimeout,
  4810. this,
  4811. [=] { updateSendButtonType(); });
  4812. }
  4813. }
  4814. bool HistoryWidget::updateCmdStartShown() {
  4815. const auto bot = (_peer && _peer->isUser() && _peer->asUser()->isBot())
  4816. ? _peer->asUser()
  4817. : nullptr;
  4818. auto cmdStartShown = false;
  4819. if (_history
  4820. && _peer
  4821. && (false
  4822. || (_peer->isChat() && _peer->asChat()->botStatus > 0)
  4823. || (_peer->isMegagroup()
  4824. && _peer->asChannel()->mgInfo->botStatus > 0))) {
  4825. if (!isBotStart()
  4826. && !isBlocked()
  4827. && !_keyboard->hasMarkup()
  4828. && !_keyboard->forceReply()
  4829. && !_editMsgId) {
  4830. if (!HasSendText(_field)) {
  4831. cmdStartShown = true;
  4832. }
  4833. }
  4834. }
  4835. constexpr auto kSmallMenuAfter = 10;
  4836. const auto commandsChanged = (_cmdStartShown != cmdStartShown);
  4837. auto buttonChanged = false;
  4838. if (!bot
  4839. || (bot->botInfo->botMenuButtonUrl.isEmpty()
  4840. && bot->botInfo->commands.empty())) {
  4841. buttonChanged = (_botMenu.button != nullptr);
  4842. _botMenu.button.destroy();
  4843. } else if (!_botMenu.button) {
  4844. buttonChanged = true;
  4845. _botMenu.text = bot->botInfo->botMenuButtonText;
  4846. _botMenu.small = (_fieldCharsCountManager.count() > kSmallMenuAfter);
  4847. if (_botMenu.small) {
  4848. if (const auto e = FirstEmoji(_botMenu.text); !e.isEmpty()) {
  4849. _botMenu.text = e;
  4850. }
  4851. }
  4852. _botMenu.button.create(
  4853. this,
  4854. (_botMenu.text.isEmpty()
  4855. ? tr::lng_bot_menu_button()
  4856. : rpl::single(_botMenu.text)),
  4857. st::historyBotMenuButton);
  4858. orderWidgets();
  4859. _botMenu.button->setTextTransform(
  4860. Ui::RoundButton::TextTransform::NoTransform);
  4861. _botMenu.button->setFullRadius(true);
  4862. _botMenu.button->setClickedCallback([=] {
  4863. const auto user = _peer ? _peer->asUser() : nullptr;
  4864. const auto bot = (user && user->isBot()) ? user : nullptr;
  4865. if (bot && !bot->botInfo->botMenuButtonUrl.isEmpty()) {
  4866. session().attachWebView().open({
  4867. .bot = bot,
  4868. .context = { .controller = controller() },
  4869. .button = {
  4870. .url = bot->botInfo->botMenuButtonUrl.toUtf8(),
  4871. },
  4872. .source = InlineBots::WebViewSourceBotMenu(),
  4873. });
  4874. } else if (_autocomplete && !_autocomplete->isHidden()) {
  4875. _autocomplete->hideAnimated();
  4876. } else if (_autocomplete) {
  4877. _autocomplete->showFiltered(_peer, "/", true);
  4878. }
  4879. });
  4880. _botMenu.button->widthValue(
  4881. ) | rpl::start_with_next([=](int width) {
  4882. if (width > st::historyBotMenuMaxWidth) {
  4883. _botMenu.button->setFullWidth(st::historyBotMenuMaxWidth);
  4884. } else {
  4885. updateFieldSize();
  4886. }
  4887. }, _botMenu.button->lifetime());
  4888. }
  4889. const auto textSmall = _fieldCharsCountManager.count() > kSmallMenuAfter;
  4890. const auto textChanged = _botMenu.button
  4891. && ((_botMenu.text != bot->botInfo->botMenuButtonText)
  4892. || (_botMenu.small != textSmall));
  4893. if (textChanged) {
  4894. _botMenu.text = bot->botInfo->botMenuButtonText;
  4895. if ((_botMenu.small = textSmall)) {
  4896. if (const auto e = FirstEmoji(_botMenu.text); !e.isEmpty()) {
  4897. _botMenu.text = e;
  4898. }
  4899. }
  4900. _botMenu.button->setText(_botMenu.text.isEmpty()
  4901. ? tr::lng_bot_menu_button()
  4902. : rpl::single(_botMenu.text));
  4903. }
  4904. _cmdStartShown = cmdStartShown;
  4905. return commandsChanged || buttonChanged || textChanged;
  4906. }
  4907. bool HistoryWidget::searchInChatEmbedded(
  4908. QString query,
  4909. Dialogs::Key chat,
  4910. PeerData *searchFrom) {
  4911. const auto peer = chat.peer(); // windows todo
  4912. if (!peer || Window::SeparateId(peer) != controller()->windowId()) {
  4913. return false;
  4914. } else if (_peer != peer) {
  4915. const auto weak = Ui::MakeWeak(this);
  4916. controller()->showPeerHistory(peer);
  4917. if (!weak) {
  4918. return false;
  4919. }
  4920. }
  4921. if (_peer != peer) {
  4922. return false;
  4923. } else if (_composeSearch) {
  4924. _composeSearch->setQuery(query);
  4925. _composeSearch->setInnerFocus();
  4926. return true;
  4927. }
  4928. switchToSearch(query);
  4929. return true;
  4930. }
  4931. void HistoryWidget::switchToSearch(QString query) {
  4932. const auto search = crl::guard(_list, [=] {
  4933. if (!_peer) {
  4934. return;
  4935. }
  4936. const auto update = [=] {
  4937. updateControlsVisibility();
  4938. updateBotKeyboard();
  4939. updateFieldPlaceholder();
  4940. updateControlsGeometry();
  4941. };
  4942. const auto from = (PeerData*)nullptr;
  4943. _composeSearch = std::make_unique<HistoryView::ComposeSearch>(
  4944. this,
  4945. controller(),
  4946. _history,
  4947. from,
  4948. query);
  4949. update();
  4950. setInnerFocus();
  4951. using Activation = HistoryView::ComposeSearch::Activation;
  4952. _composeSearch->activations(
  4953. ) | rpl::start_with_next([=](Activation activation) {
  4954. const auto item = activation.item;
  4955. auto params = ::Window::SectionShow(
  4956. ::Window::SectionShow::Way::ClearStack);
  4957. params.highlightPart = { activation.query };
  4958. params.highlightPartOffsetHint = kSearchQueryOffsetHint;
  4959. controller()->showPeerHistory(
  4960. item->history()->peer->id,
  4961. params,
  4962. item->fullId().msg);
  4963. }, _composeSearch->lifetime());
  4964. _composeSearch->destroyRequests(
  4965. ) | rpl::take(1) | rpl::start_with_next([=] {
  4966. _composeSearch = nullptr;
  4967. update();
  4968. setInnerFocus();
  4969. }, _composeSearch->lifetime());
  4970. });
  4971. if (!preventsClose(search)) {
  4972. search();
  4973. }
  4974. }
  4975. bool HistoryWidget::kbWasHidden() const {
  4976. return _history
  4977. && (_keyboard->forMsgId()
  4978. == FullMsgId(
  4979. _history->peer->id,
  4980. _history->lastKeyboardHiddenId));
  4981. }
  4982. void HistoryWidget::showKeyboardHideButton() {
  4983. _botKeyboardHide->setVisible(!_peer->isUser()
  4984. || !_keyboard->persistent());
  4985. }
  4986. void HistoryWidget::toggleKeyboard(bool manual) {
  4987. const auto fieldEnabled = canWriteMessage() && !_showAnimation;
  4988. if (_kbShown || _kbReplyTo) {
  4989. _botKeyboardHide->hide();
  4990. if (_kbShown) {
  4991. if (fieldEnabled) {
  4992. _botKeyboardShow->show();
  4993. }
  4994. if (manual && _history) {
  4995. _history->lastKeyboardHiddenId = _keyboard->forMsgId().msg;
  4996. }
  4997. _kbScroll->hide();
  4998. _kbShown = false;
  4999. _field->setMaxHeight(computeMaxFieldHeight());
  5000. _kbReplyTo = nullptr;
  5001. if (!readyToForward()
  5002. && !_previewDrawPreview
  5003. && !_editMsgId
  5004. && !_replyTo) {
  5005. _fieldBarCancel->hide();
  5006. updateMouseTracking();
  5007. }
  5008. } else {
  5009. if (_history) {
  5010. _history->clearLastKeyboard();
  5011. } else {
  5012. updateBotKeyboard();
  5013. }
  5014. }
  5015. } else if (!_keyboard->hasMarkup() && _keyboard->forceReply()) {
  5016. _botKeyboardHide->hide();
  5017. _botKeyboardShow->hide();
  5018. if (fieldEnabled) {
  5019. _botCommandStart->show();
  5020. }
  5021. _kbScroll->hide();
  5022. _kbShown = false;
  5023. _field->setMaxHeight(computeMaxFieldHeight());
  5024. _kbReplyTo = (false
  5025. || _peer->isChat()
  5026. || _peer->isChannel()
  5027. || _keyboard->forceReply())
  5028. ? session().data().message(_keyboard->forMsgId())
  5029. : nullptr;
  5030. if (_kbReplyTo && !_editMsgId && !_replyTo && fieldEnabled) {
  5031. updateReplyToName();
  5032. updateReplyEditText(_kbReplyTo);
  5033. }
  5034. if (manual && _history) {
  5035. _history->lastKeyboardHiddenId = 0;
  5036. }
  5037. } else if (fieldEnabled) {
  5038. showKeyboardHideButton();
  5039. _botKeyboardShow->hide();
  5040. _kbScroll->show();
  5041. _kbShown = true;
  5042. const auto maxheight = computeMaxFieldHeight();
  5043. const auto kbheight = qMin(
  5044. _keyboard->height(),
  5045. maxheight - (maxheight / 2));
  5046. _field->setMaxHeight(maxheight - kbheight);
  5047. _kbReplyTo = (false
  5048. || _peer->isChat()
  5049. || _peer->isChannel()
  5050. || _keyboard->forceReply())
  5051. ? session().data().message(_keyboard->forMsgId())
  5052. : nullptr;
  5053. if (_kbReplyTo && !_editMsgId && !_replyTo) {
  5054. updateReplyToName();
  5055. updateReplyEditText(_kbReplyTo);
  5056. }
  5057. if (manual && _history) {
  5058. _history->lastKeyboardHiddenId = 0;
  5059. }
  5060. }
  5061. updateControlsGeometry();
  5062. updateFieldPlaceholder();
  5063. if (_botKeyboardHide->isHidden()
  5064. && canWriteMessage()
  5065. && !_showAnimation) {
  5066. _tabbedSelectorToggle->show();
  5067. } else {
  5068. _tabbedSelectorToggle->hide();
  5069. }
  5070. updateField();
  5071. }
  5072. void HistoryWidget::startBotCommand() {
  5073. setFieldText(
  5074. { u"/"_q, TextWithTags::Tags() },
  5075. 0,
  5076. Ui::InputField::HistoryAction::NewEntry);
  5077. }
  5078. void HistoryWidget::setMembersShowAreaActive(bool active) {
  5079. if (!active) {
  5080. _membersDropdownShowTimer.cancel();
  5081. }
  5082. if (active && _peer && (_peer->isChat() || _peer->isMegagroup())) {
  5083. if (_membersDropdown) {
  5084. _membersDropdown->otherEnter();
  5085. } else if (!_membersDropdownShowTimer.isActive()) {
  5086. _membersDropdownShowTimer.callOnce(kShowMembersDropdownTimeoutMs);
  5087. }
  5088. } else if (_membersDropdown) {
  5089. _membersDropdown->otherLeave();
  5090. }
  5091. }
  5092. void HistoryWidget::showMembersDropdown() {
  5093. if (!_peer) {
  5094. return;
  5095. }
  5096. if (!_membersDropdown) {
  5097. _membersDropdown.create(this, st::membersInnerDropdown);
  5098. _membersDropdown->setOwnedWidget(
  5099. object_ptr<Profile::GroupMembersWidget>(
  5100. this,
  5101. controller(),
  5102. _peer,
  5103. st::membersInnerItem));
  5104. _membersDropdown->resizeToWidth(st::membersInnerWidth);
  5105. _membersDropdown->setMaxHeight(countMembersDropdownHeightMax());
  5106. _membersDropdown->moveToLeft(0, _topBar->height());
  5107. _membersDropdown->setHiddenCallback([this] {
  5108. _membersDropdown.destroyDelayed();
  5109. });
  5110. }
  5111. _membersDropdown->otherEnter();
  5112. }
  5113. bool HistoryWidget::pushTabbedSelectorToThirdSection(
  5114. not_null<Data::Thread*> thread,
  5115. const Window::SectionShow &params) {
  5116. if (!_tabbedPanel) {
  5117. return true;
  5118. } else if (!Data::CanSendAnyOf(
  5119. thread,
  5120. Data::TabbedPanelSendRestrictions())) {
  5121. Core::App().settings().setTabbedReplacedWithInfo(true);
  5122. controller()->showPeerInfo(thread, params.withThirdColumn());
  5123. return false;
  5124. }
  5125. Core::App().settings().setTabbedReplacedWithInfo(false);
  5126. controller()->resizeForThirdSection();
  5127. controller()->showSection(
  5128. std::make_shared<ChatHelpers::TabbedMemento>(),
  5129. params.withThirdColumn());
  5130. return true;
  5131. }
  5132. bool HistoryWidget::returnTabbedSelector() {
  5133. createTabbedPanel();
  5134. moveFieldControls();
  5135. return true;
  5136. }
  5137. void HistoryWidget::createTabbedPanel() {
  5138. setTabbedPanel(std::make_unique<TabbedPanel>(
  5139. this,
  5140. controller(),
  5141. controller()->tabbedSelector()));
  5142. }
  5143. void HistoryWidget::setTabbedPanel(std::unique_ptr<TabbedPanel> panel) {
  5144. _tabbedPanel = std::move(panel);
  5145. if (const auto raw = _tabbedPanel.get()) {
  5146. _tabbedSelectorToggle->installEventFilter(raw);
  5147. _tabbedSelectorToggle->setColorOverrides(nullptr, nullptr, nullptr);
  5148. } else {
  5149. _tabbedSelectorToggle->setColorOverrides(
  5150. &st::historyAttachEmojiActive,
  5151. &st::historyRecordVoiceFgActive,
  5152. &st::historyRecordVoiceRippleBgActive);
  5153. }
  5154. }
  5155. bool HistoryWidget::preventsClose(Fn<void()> &&continueCallback) const {
  5156. if (_voiceRecordBar->isActive()) {
  5157. _voiceRecordBar->showDiscardBox(std::move(continueCallback));
  5158. return true;
  5159. }
  5160. return false;
  5161. }
  5162. void HistoryWidget::toggleTabbedSelectorMode() {
  5163. if (!_history) {
  5164. return;
  5165. }
  5166. if (_tabbedPanel) {
  5167. if (controller()->canShowThirdSection()
  5168. && !controller()->adaptive().isOneColumn()) {
  5169. Core::App().settings().setTabbedSelectorSectionEnabled(true);
  5170. Core::App().saveSettingsDelayed();
  5171. pushTabbedSelectorToThirdSection(
  5172. _history,
  5173. Window::SectionShow::Way::ClearStack);
  5174. } else {
  5175. _tabbedPanel->toggleAnimated();
  5176. }
  5177. } else {
  5178. controller()->closeThirdSection();
  5179. }
  5180. }
  5181. void HistoryWidget::recountChatWidth() {
  5182. const auto layout = (width() < st::adaptiveChatWideWidth)
  5183. ? Window::Adaptive::ChatLayout::Normal
  5184. : Window::Adaptive::ChatLayout::Wide;
  5185. controller()->adaptive().setChatLayout(layout);
  5186. }
  5187. int HistoryWidget::fieldHeight() const {
  5188. return (_canSendTexts || _editMsgId)
  5189. ? _field->height()
  5190. : (st::historySendSize.height() - 2 * st::historySendPadding);
  5191. }
  5192. bool HistoryWidget::fieldOrDisabledShown() const {
  5193. return !_field->isHidden() || _fieldDisabled;
  5194. }
  5195. void HistoryWidget::moveFieldControls() {
  5196. auto keyboardHeight = 0;
  5197. auto bottom = height();
  5198. auto maxKeyboardHeight = computeMaxFieldHeight() - fieldHeight();
  5199. _keyboard->resizeToWidth(width(), maxKeyboardHeight);
  5200. if (_kbShown) {
  5201. keyboardHeight = qMin(_keyboard->height(), maxKeyboardHeight);
  5202. bottom -= keyboardHeight;
  5203. _kbScroll->setGeometryToLeft(0, bottom, width(), keyboardHeight);
  5204. }
  5205. // (_botMenu.button) (_attachToggle|_replaceMedia) (_sendAs) ---- _inlineResults ------------------------------ _tabbedPanel ------ _fieldBarCancel
  5206. // (_attachDocument|_attachPhoto) _field (_ttlInfo) (_scheduled) (_silent|_cmdStart|_kbShow) (_kbHide|_tabbedSelectorToggle) _send
  5207. // (_botStart|_unblock|_joinChannel|_muteUnmute|_reportMessages)
  5208. auto buttonsBottom = bottom - _attachToggle->height();
  5209. auto left = st::historySendRight;
  5210. if (_botMenu.button) {
  5211. const auto skip = st::historyBotMenuSkip;
  5212. _botMenu.button->moveToLeft(left + skip, buttonsBottom + skip);
  5213. left += skip + _botMenu.button->width();
  5214. }
  5215. if (_replaceMedia) {
  5216. _replaceMedia->moveToLeft(left, buttonsBottom);
  5217. }
  5218. _attachToggle->moveToLeft(left, buttonsBottom);
  5219. left += _attachToggle->width();
  5220. if (_sendAs) {
  5221. _sendAs->moveToLeft(left, buttonsBottom);
  5222. left += _sendAs->width();
  5223. }
  5224. _field->moveToLeft(
  5225. left,
  5226. bottom - _field->height() - st::historySendPadding);
  5227. if (_fieldDisabled) {
  5228. _fieldDisabled->moveToLeft(
  5229. left,
  5230. bottom - fieldHeight() - st::historySendPadding);
  5231. }
  5232. auto right = st::historySendRight;
  5233. _send->moveToRight(right, buttonsBottom); right += _send->width();
  5234. _voiceRecordBar->moveToLeft(0, bottom - _voiceRecordBar->height());
  5235. _tabbedSelectorToggle->moveToRight(right, buttonsBottom);
  5236. _botKeyboardHide->moveToRight(right, buttonsBottom);
  5237. right += _botKeyboardHide->width();
  5238. _botKeyboardShow->moveToRight(right, buttonsBottom);
  5239. _botCommandStart->moveToRight(right, buttonsBottom);
  5240. if (_silent) {
  5241. _silent->moveToRight(right, buttonsBottom);
  5242. }
  5243. const auto kbShowShown = _history && !_kbShown && _keyboard->hasMarkup();
  5244. if (kbShowShown || _cmdStartShown || _silent) {
  5245. right += _botCommandStart->width();
  5246. }
  5247. if (_scheduled) {
  5248. _scheduled->moveToRight(right, buttonsBottom);
  5249. right += _scheduled->width();
  5250. }
  5251. if (_ttlInfo) {
  5252. _ttlInfo->move(width() - right - _ttlInfo->width(), buttonsBottom);
  5253. }
  5254. _fieldBarCancel->moveToRight(
  5255. 0,
  5256. _field->y() - st::historySendPadding - _fieldBarCancel->height());
  5257. if (_inlineResults) {
  5258. _inlineResults->moveBottom(_field->y() - st::historySendPadding);
  5259. }
  5260. if (_tabbedPanel) {
  5261. _tabbedPanel->moveBottomRight(buttonsBottom, width());
  5262. }
  5263. if (_attachBotsMenu) {
  5264. _attachBotsMenu->moveToLeft(
  5265. 0,
  5266. buttonsBottom - _attachBotsMenu->height());
  5267. }
  5268. const auto fullWidthButtonRect = myrtlrect(
  5269. 0,
  5270. bottom - _botStart->height(),
  5271. width(),
  5272. _botStart->height());
  5273. _botStart->setGeometry(fullWidthButtonRect);
  5274. _unblock->setGeometry(fullWidthButtonRect);
  5275. _joinChannel->setGeometry(fullWidthButtonRect);
  5276. _muteUnmute->setGeometry(fullWidthButtonRect);
  5277. _reportMessages->setGeometry(fullWidthButtonRect);
  5278. if (_sendRestriction) {
  5279. _sendRestriction->setGeometry(fullWidthButtonRect);
  5280. }
  5281. }
  5282. void HistoryWidget::updateFieldSize() {
  5283. const auto kbShowShown = _history && !_kbShown && _keyboard->hasMarkup();
  5284. auto fieldWidth = width()
  5285. - _attachToggle->width()
  5286. - st::historySendRight
  5287. - _send->width()
  5288. - _tabbedSelectorToggle->width();
  5289. if (_botMenu.button) {
  5290. fieldWidth -= st::historyBotMenuSkip + _botMenu.button->width();
  5291. }
  5292. if (_sendAs) {
  5293. fieldWidth -= _sendAs->width();
  5294. }
  5295. if (kbShowShown) {
  5296. fieldWidth -= _botKeyboardShow->width();
  5297. }
  5298. if (_cmdStartShown) {
  5299. fieldWidth -= _botCommandStart->width();
  5300. }
  5301. if (_silent && _silent->isVisible()) {
  5302. fieldWidth -= _silent->width();
  5303. }
  5304. if (_scheduled && _scheduled->isVisible()) {
  5305. fieldWidth -= _scheduled->width();
  5306. }
  5307. if (_ttlInfo && _ttlInfo->isVisible()) {
  5308. fieldWidth -= _ttlInfo->width();
  5309. }
  5310. if (_fieldDisabled) {
  5311. _fieldDisabled->resize(width(), st::historySendSize.height());
  5312. }
  5313. if (_field->width() != fieldWidth) {
  5314. _field->resize(fieldWidth, _field->height());
  5315. } else {
  5316. moveFieldControls();
  5317. }
  5318. }
  5319. void HistoryWidget::clearInlineBot() {
  5320. if (_inlineBot || _inlineLookingUpBot) {
  5321. _inlineBot = nullptr;
  5322. _inlineLookingUpBot = false;
  5323. inlineBotChanged();
  5324. _field->finishAnimating();
  5325. }
  5326. if (_inlineResults) {
  5327. _inlineResults->clearInlineBot();
  5328. }
  5329. if (_autocomplete) {
  5330. _autocomplete->requestRefresh();
  5331. }
  5332. }
  5333. void HistoryWidget::inlineBotChanged() {
  5334. bool isInlineBot = showInlineBotCancel();
  5335. if (_isInlineBot != isInlineBot) {
  5336. _isInlineBot = isInlineBot;
  5337. updateFieldPlaceholder();
  5338. updateFieldSubmitSettings();
  5339. updateControlsVisibility();
  5340. }
  5341. }
  5342. void HistoryWidget::fieldResized() {
  5343. moveFieldControls();
  5344. updateHistoryGeometry();
  5345. updateField();
  5346. }
  5347. void HistoryWidget::fieldFocused() {
  5348. if (_list) {
  5349. _list->clearSelected(true);
  5350. }
  5351. }
  5352. void HistoryWidget::updateFieldPlaceholder() {
  5353. _voiceRecordBar->setPauseInsteadSend(_history
  5354. && _history->peer->starsPerMessageChecked() > 0);
  5355. if (!_editMsgId && _inlineBot && !_inlineLookingUpBot) {
  5356. _field->setPlaceholder(
  5357. rpl::single(_inlineBot->botInfo->inlinePlaceholder.mid(1)),
  5358. _inlineBotUsername.size() + 2);
  5359. return;
  5360. }
  5361. _field->setPlaceholder([&]() -> rpl::producer<QString> {
  5362. const auto peer = _history ? _history->peer.get() : nullptr;
  5363. if (_editMsgId) {
  5364. return tr::lng_edit_message_text();
  5365. } else if (!peer) {
  5366. return tr::lng_message_ph();
  5367. } else if ((_kbShown || _keyboard->forceReply())
  5368. && !_keyboard->placeholder().isEmpty()) {
  5369. return rpl::single(_keyboard->placeholder());
  5370. } else if (const auto stars = peer->starsPerMessageChecked()) {
  5371. return tr::lng_message_paid_ph(
  5372. lt_amount,
  5373. tr::lng_prize_credits_amount(
  5374. lt_count,
  5375. rpl::single(stars * 1.)));
  5376. } else if (const auto channel = peer->asChannel()) {
  5377. const auto topic = resolveReplyToTopic();
  5378. const auto topicRootId = topic
  5379. ? topic->rootId()
  5380. : channel->forum()
  5381. ? resolveReplyToTopicRootId()
  5382. : MsgId();
  5383. if (topicRootId) {
  5384. auto title = rpl::single(topic
  5385. ? topic->title()
  5386. : (topicRootId == Data::ForumTopic::kGeneralId)
  5387. ? u"General"_q
  5388. : u"Topic"_q
  5389. ) | rpl::then(session().changes().topicUpdates(
  5390. Data::TopicUpdate::Flag::Title
  5391. ) | rpl::filter([=](const Data::TopicUpdate &update) {
  5392. return (update.topic->peer() == channel)
  5393. && (update.topic->rootId() == topicRootId);
  5394. }) | rpl::map([=](const Data::TopicUpdate &update) {
  5395. return update.topic->title();
  5396. }));
  5397. const auto phrase = replyTo().messageId
  5398. ? tr::lng_forum_reply_in
  5399. : tr::lng_forum_message_in;
  5400. return phrase(lt_topic, std::move(title));
  5401. } else if (channel->isBroadcast()) {
  5402. return session().data().notifySettings().silentPosts(channel)
  5403. ? tr::lng_broadcast_silent_ph()
  5404. : tr::lng_broadcast_ph();
  5405. } else if (channel->adminRights() & ChatAdminRight::Anonymous) {
  5406. return tr::lng_send_anonymous_ph();
  5407. } else {
  5408. return tr::lng_message_ph();
  5409. }
  5410. } else {
  5411. return tr::lng_message_ph();
  5412. }
  5413. }());
  5414. updateSendButtonType();
  5415. }
  5416. bool HistoryWidget::showSendingFilesError(
  5417. const Ui::PreparedList &list) const {
  5418. return showSendingFilesError(list, std::nullopt);
  5419. }
  5420. bool HistoryWidget::showSendingFilesError(
  5421. const Ui::PreparedList &list,
  5422. std::optional<bool> compress) const {
  5423. const auto error = [&]() -> Data::SendError {
  5424. const auto error = _peer
  5425. ? Data::FileRestrictionError(_peer, list, compress)
  5426. : Data::SendError();
  5427. if (!_peer || error) {
  5428. return error;
  5429. } else if (const auto left = _peer->slowmodeSecondsLeft()) {
  5430. return tr::lng_slowmode_enabled(
  5431. tr::now,
  5432. lt_left,
  5433. Ui::FormatDurationWordsSlowmode(left));
  5434. }
  5435. using Error = Ui::PreparedList::Error;
  5436. switch (list.error) {
  5437. case Error::None: return QString();
  5438. case Error::EmptyFile:
  5439. case Error::Directory:
  5440. case Error::NonLocalUrl: return tr::lng_send_image_empty(
  5441. tr::now,
  5442. lt_name,
  5443. list.errorData);
  5444. case Error::TooLargeFile: return u"(toolarge)"_q;
  5445. }
  5446. return tr::lng_forward_send_files_cant(tr::now);
  5447. }();
  5448. if (!error) {
  5449. return false;
  5450. } else if (error.text == u"(toolarge)"_q) {
  5451. const auto fileSize = list.files.back().size;
  5452. controller()->show(
  5453. Box(FileSizeLimitBox, &session(), fileSize, nullptr));
  5454. return true;
  5455. }
  5456. Data::ShowSendErrorToast(controller(), _peer, error);
  5457. return true;
  5458. }
  5459. MsgId HistoryWidget::resolveReplyToTopicRootId() {
  5460. Expects(_peer != nullptr);
  5461. const auto replyToInfo = replyTo();
  5462. const auto replyToMessage = (replyToInfo.messageId.peer == _peer->id)
  5463. ? session().data().message(replyToInfo.messageId)
  5464. : nullptr;
  5465. const auto result = replyToMessage
  5466. ? replyToMessage->topicRootId()
  5467. : replyToInfo.topicRootId;
  5468. if (result
  5469. && _peer->isForum()
  5470. && !_peer->forumTopicFor(result)
  5471. && _topicsRequested.emplace(result).second) {
  5472. _peer->forum()->requestTopic(result, crl::guard(_list, [=] {
  5473. updateCanSendMessage();
  5474. updateFieldPlaceholder();
  5475. _topicsRequested.remove(result);
  5476. }));
  5477. }
  5478. return result;
  5479. }
  5480. Data::ForumTopic *HistoryWidget::resolveReplyToTopic() {
  5481. return _peer
  5482. ? _peer->forumTopicFor(resolveReplyToTopicRootId())
  5483. : nullptr;
  5484. }
  5485. bool HistoryWidget::showSendMessageError(
  5486. const TextWithTags &textWithTags,
  5487. bool ignoreSlowmodeCountdown,
  5488. Fn<void(int starsApproved)> withPaymentApproved,
  5489. int starsApproved) {
  5490. if (!_canSendMessages) {
  5491. return false;
  5492. }
  5493. const auto topicRootId = resolveReplyToTopicRootId();
  5494. auto request = SendingErrorRequest{
  5495. .topicRootId = topicRootId,
  5496. .forward = &_forwardPanel->items(),
  5497. .text = &textWithTags,
  5498. .ignoreSlowmodeCountdown = ignoreSlowmodeCountdown,
  5499. };
  5500. request.messagesCount = ComputeSendingMessagesCount(_history, request);
  5501. const auto error = GetErrorForSending(_peer, request);
  5502. if (error) {
  5503. Data::ShowSendErrorToast(controller(), _peer, error);
  5504. return true;
  5505. }
  5506. return withPaymentApproved
  5507. && !checkSendPayment(
  5508. request.messagesCount,
  5509. starsApproved,
  5510. withPaymentApproved);
  5511. }
  5512. bool HistoryWidget::confirmSendingFiles(const QStringList &files) {
  5513. return confirmSendingFiles(files, QString());
  5514. }
  5515. bool HistoryWidget::confirmSendingFiles(not_null<const QMimeData*> data) {
  5516. return confirmSendingFiles(data, std::nullopt);
  5517. }
  5518. bool HistoryWidget::confirmSendingFiles(
  5519. const QStringList &files,
  5520. const QString &insertTextOnCancel) {
  5521. const auto premium = controller()->session().user()->isPremium();
  5522. return confirmSendingFiles(
  5523. Storage::PrepareMediaList(files, st::sendMediaPreviewSize, premium),
  5524. insertTextOnCancel);
  5525. }
  5526. bool HistoryWidget::confirmSendingFiles(
  5527. Ui::PreparedList &&list,
  5528. const QString &insertTextOnCancel) {
  5529. if (_editMsgId) {
  5530. if (_canReplaceMedia || _canAddMedia) {
  5531. EditCaptionBox::StartMediaReplace(
  5532. controller(),
  5533. { _history->peer->id, _editMsgId },
  5534. std::move(list),
  5535. _field->getTextWithTags(),
  5536. _mediaEditManager.spoilered(),
  5537. _mediaEditManager.invertCaption(),
  5538. crl::guard(_list, [=] { cancelEdit(); }));
  5539. return true;
  5540. }
  5541. controller()->showToast(tr::lng_edit_caption_attach(tr::now));
  5542. return false;
  5543. } else if (showSendingFilesError(list)) {
  5544. return false;
  5545. }
  5546. const auto cursor = _field->textCursor();
  5547. const auto position = cursor.position();
  5548. const auto anchor = cursor.anchor();
  5549. const auto text = _field->getTextWithTags();
  5550. auto box = Box<SendFilesBox>(
  5551. controller(),
  5552. std::move(list),
  5553. text,
  5554. _peer,
  5555. Api::SendType::Normal,
  5556. sendMenuDetails());
  5557. _field->setTextWithTags({});
  5558. box->setConfirmedCallback(crl::guard(this, [=](
  5559. Ui::PreparedList &&list,
  5560. Ui::SendFilesWay way,
  5561. TextWithTags &&caption,
  5562. Api::SendOptions options,
  5563. bool ctrlShiftEnter) {
  5564. sendingFilesConfirmed(
  5565. std::move(list),
  5566. way,
  5567. std::move(caption),
  5568. options,
  5569. ctrlShiftEnter);
  5570. }));
  5571. box->setCancelledCallback(crl::guard(this, [=] {
  5572. _field->setTextWithTags(text);
  5573. auto cursor = _field->textCursor();
  5574. cursor.setPosition(anchor);
  5575. if (position != anchor) {
  5576. cursor.setPosition(position, QTextCursor::KeepAnchor);
  5577. }
  5578. _field->setTextCursor(cursor);
  5579. if (Ui::InsertTextOnImageCancel(insertTextOnCancel)) {
  5580. _field->textCursor().insertText(insertTextOnCancel);
  5581. }
  5582. }));
  5583. Window::ActivateWindow(controller());
  5584. controller()->show(std::move(box));
  5585. return true;
  5586. }
  5587. void HistoryWidget::sendingFilesConfirmed(
  5588. Ui::PreparedList &&list,
  5589. Ui::SendFilesWay way,
  5590. TextWithTags &&caption,
  5591. Api::SendOptions options,
  5592. bool ctrlShiftEnter) {
  5593. Expects(list.filesToProcess.empty());
  5594. const auto compress = way.sendImagesAsPhotos();
  5595. if (showSendingFilesError(list, compress)) {
  5596. return;
  5597. }
  5598. auto groups = DivideByGroups(
  5599. std::move(list),
  5600. way,
  5601. _peer->slowmodeApplied());
  5602. auto bundle = PrepareFilesBundle(
  5603. std::move(groups),
  5604. way,
  5605. std::move(caption),
  5606. ctrlShiftEnter);
  5607. sendingFilesConfirmed(std::move(bundle), options);
  5608. }
  5609. void HistoryWidget::sendingFilesConfirmed(
  5610. std::shared_ptr<Ui::PreparedBundle> bundle,
  5611. Api::SendOptions options) {
  5612. const auto withPaymentApproved = [=](int approved) {
  5613. auto copy = options;
  5614. copy.starsApproved = approved;
  5615. sendingFilesConfirmed(bundle, copy);
  5616. };
  5617. const auto checked = checkSendPayment(
  5618. bundle->totalCount,
  5619. options.starsApproved,
  5620. withPaymentApproved);
  5621. if (!checked) {
  5622. return;
  5623. }
  5624. const auto compress = bundle->way.sendImagesAsPhotos();
  5625. const auto type = compress ? SendMediaType::Photo : SendMediaType::File;
  5626. auto action = prepareSendAction(options);
  5627. action.clearDraft = false;
  5628. if (bundle->sendComment) {
  5629. auto message = Api::MessageToSend(action);
  5630. message.textWithTags = base::take(bundle->caption);
  5631. session().api().sendMessage(std::move(message));
  5632. }
  5633. for (auto &group : bundle->groups) {
  5634. const auto album = (group.type != Ui::AlbumType::None)
  5635. ? std::make_shared<SendingAlbum>()
  5636. : nullptr;
  5637. session().api().sendFiles(
  5638. std::move(group.list),
  5639. type,
  5640. base::take(bundle->caption),
  5641. album,
  5642. action);
  5643. }
  5644. }
  5645. bool HistoryWidget::confirmSendingFiles(
  5646. QImage &&image,
  5647. QByteArray &&content,
  5648. std::optional<bool> overrideSendImagesAsPhotos,
  5649. const QString &insertTextOnCancel) {
  5650. if (image.isNull()) {
  5651. return false;
  5652. }
  5653. auto list = Storage::PrepareMediaFromImage(
  5654. std::move(image),
  5655. std::move(content),
  5656. st::sendMediaPreviewSize);
  5657. list.overrideSendImagesAsPhotos = overrideSendImagesAsPhotos;
  5658. return confirmSendingFiles(std::move(list), insertTextOnCancel);
  5659. }
  5660. bool HistoryWidget::canSendFiles(not_null<const QMimeData*> data) const {
  5661. if (!canWriteMessage()) {
  5662. return false;
  5663. } else if (data->hasImage()) {
  5664. return true;
  5665. } else if (const auto urls = Core::ReadMimeUrls(data); !urls.empty()) {
  5666. if (ranges::all_of(urls, &QUrl::isLocalFile)) {
  5667. return true;
  5668. }
  5669. }
  5670. return false;
  5671. }
  5672. bool HistoryWidget::confirmSendingFiles(
  5673. not_null<const QMimeData*> data,
  5674. std::optional<bool> overrideSendImagesAsPhotos,
  5675. const QString &insertTextOnCancel) {
  5676. if (!canWriteMessage()) {
  5677. return false;
  5678. }
  5679. const auto hasImage = data->hasImage();
  5680. const auto premium = controller()->session().user()->isPremium();
  5681. if (const auto urls = Core::ReadMimeUrls(data); !urls.empty()) {
  5682. auto list = Storage::PrepareMediaList(
  5683. urls,
  5684. st::sendMediaPreviewSize,
  5685. premium);
  5686. if (list.error != Ui::PreparedList::Error::NonLocalUrl) {
  5687. if (list.error == Ui::PreparedList::Error::None
  5688. || !hasImage) {
  5689. const auto emptyTextOnCancel = QString();
  5690. list.overrideSendImagesAsPhotos = overrideSendImagesAsPhotos;
  5691. confirmSendingFiles(std::move(list), emptyTextOnCancel);
  5692. return true;
  5693. }
  5694. }
  5695. }
  5696. if (auto read = Core::ReadMimeImage(data)) {
  5697. confirmSendingFiles(
  5698. std::move(read.image),
  5699. std::move(read.content),
  5700. overrideSendImagesAsPhotos,
  5701. insertTextOnCancel);
  5702. return true;
  5703. }
  5704. return false;
  5705. }
  5706. void HistoryWidget::uploadFile(
  5707. const QByteArray &fileContent,
  5708. SendMediaType type) {
  5709. if (!canWriteMessage()) return;
  5710. session().api().sendFile(fileContent, type, prepareSendAction({}));
  5711. }
  5712. void HistoryWidget::handleHistoryChange(not_null<const History*> history) {
  5713. if (_list && (_history == history || _migrated == history)) {
  5714. handlePendingHistoryUpdate();
  5715. updateBotKeyboard();
  5716. if (!_scroll->isHidden()) {
  5717. const auto unblock = isBlocked();
  5718. const auto botStart = isBotStart();
  5719. const auto joinChannel = isJoinChannel();
  5720. const auto muteUnmute = isMuteUnmute();
  5721. const auto reportMessages = isReportMessages();
  5722. const auto update = false
  5723. || (_reportMessages->isHidden() == reportMessages)
  5724. || (!reportMessages && _unblock->isHidden() == unblock)
  5725. || (!reportMessages
  5726. && !unblock
  5727. && _botStart->isHidden() == botStart)
  5728. || (!reportMessages
  5729. && !unblock
  5730. && !botStart
  5731. && _joinChannel->isHidden() == joinChannel)
  5732. || (!reportMessages
  5733. && !unblock
  5734. && !botStart
  5735. && !joinChannel
  5736. && _muteUnmute->isHidden() == muteUnmute);
  5737. if (update) {
  5738. updateControlsVisibility();
  5739. updateControlsGeometry();
  5740. }
  5741. }
  5742. }
  5743. }
  5744. QPixmap HistoryWidget::grabForShowAnimation(
  5745. const Window::SectionSlideParams &params) {
  5746. if (params.withTopBarShadow) {
  5747. _topShadow->hide();
  5748. }
  5749. _inGrab = true;
  5750. updateControlsGeometry();
  5751. auto result = Ui::GrabWidget(this);
  5752. _inGrab = false;
  5753. updateControlsGeometry();
  5754. if (params.withTopBarShadow) {
  5755. _topShadow->show();
  5756. }
  5757. return result;
  5758. }
  5759. bool HistoryWidget::skipItemRepaint() {
  5760. auto ms = crl::now();
  5761. if (_lastScrolled + kSkipRepaintWhileScrollMs <= ms) {
  5762. return false;
  5763. }
  5764. _updateHistoryItems.callOnce(
  5765. _lastScrolled + kSkipRepaintWhileScrollMs - ms);
  5766. return true;
  5767. }
  5768. void HistoryWidget::updateHistoryItemsByTimer() {
  5769. if (!_list) {
  5770. return;
  5771. }
  5772. auto ms = crl::now();
  5773. if (_lastScrolled + kSkipRepaintWhileScrollMs <= ms) {
  5774. _list->update();
  5775. } else {
  5776. _updateHistoryItems.callOnce(
  5777. _lastScrolled + kSkipRepaintWhileScrollMs - ms);
  5778. }
  5779. }
  5780. void HistoryWidget::handlePendingHistoryUpdate() {
  5781. if (hasPendingResizedItems() || _updateHistoryGeometryRequired) {
  5782. updateHistoryGeometry();
  5783. _list->update();
  5784. }
  5785. }
  5786. void HistoryWidget::resizeEvent(QResizeEvent *e) {
  5787. //updateTabbedSelectorSectionShown();
  5788. recountChatWidth();
  5789. updateControlsGeometry();
  5790. }
  5791. void HistoryWidget::updateControlsGeometry() {
  5792. _topBar->resizeToWidth(width());
  5793. _topBar->moveToLeft(0, 0);
  5794. _voiceRecordBar->resizeToWidth(width());
  5795. moveFieldControls();
  5796. const auto groupCallTop = _topBar->bottomNoMargins();
  5797. if (_groupCallBar) {
  5798. _groupCallBar->move(0, groupCallTop);
  5799. _groupCallBar->resizeToWidth(width());
  5800. }
  5801. const auto requestsTop = groupCallTop
  5802. + (_groupCallBar ? _groupCallBar->height() : 0);
  5803. if (_requestsBar) {
  5804. _requestsBar->move(0, requestsTop);
  5805. _requestsBar->resizeToWidth(width());
  5806. }
  5807. const auto pinnedBarTop = requestsTop
  5808. + (_requestsBar ? _requestsBar->height() : 0);
  5809. if (_pinnedBar) {
  5810. _pinnedBar->move(0, pinnedBarTop);
  5811. _pinnedBar->resizeToWidth(width());
  5812. }
  5813. const auto sponsoredMessageBarTop = pinnedBarTop
  5814. + (_pinnedBar ? _pinnedBar->height() : 0);
  5815. if (_sponsoredMessageBar) {
  5816. _sponsoredMessageBar->move(0, sponsoredMessageBarTop);
  5817. _sponsoredMessageBar->resizeToWidth(width());
  5818. }
  5819. const auto translateTop = sponsoredMessageBarTop
  5820. + (_sponsoredMessageBar ? _sponsoredMessageBar->height() : 0);
  5821. if (_translateBar) {
  5822. _translateBar->move(0, translateTop);
  5823. _translateBar->resizeToWidth(width());
  5824. }
  5825. const auto paysStatusTop = translateTop
  5826. + (_translateBar ? _translateBar->height() : 0);
  5827. if (_paysStatus) {
  5828. _paysStatus->bar().move(0, paysStatusTop);
  5829. }
  5830. const auto contactStatusTop = paysStatusTop
  5831. + (_paysStatus ? _paysStatus->bar().height() : 0);
  5832. if (_contactStatus) {
  5833. _contactStatus->bar().move(0, contactStatusTop);
  5834. }
  5835. const auto businessBotTop = contactStatusTop
  5836. + (_contactStatus ? _contactStatus->bar().height() : 0);
  5837. if (_businessBotStatus) {
  5838. _businessBotStatus->bar().move(0, businessBotTop);
  5839. }
  5840. const auto scrollAreaTop = businessBotTop
  5841. + (_businessBotStatus ? _businessBotStatus->bar().height() : 0);
  5842. if (_scroll->y() != scrollAreaTop) {
  5843. _scroll->moveToLeft(0, scrollAreaTop);
  5844. if (_autocomplete) {
  5845. _autocomplete->setBoundings(_scroll->geometry());
  5846. }
  5847. if (_supportAutocomplete) {
  5848. _supportAutocomplete->setBoundings(_scroll->geometry());
  5849. }
  5850. }
  5851. updateHistoryGeometry(false, false, { ScrollChangeAdd, _topDelta });
  5852. updateFieldSize();
  5853. _cornerButtons.updatePositions();
  5854. if (_membersDropdown) {
  5855. _membersDropdown->setMaxHeight(countMembersDropdownHeightMax());
  5856. }
  5857. const auto isOneColumn = controller()->adaptive().isOneColumn();
  5858. const auto isThreeColumn = controller()->adaptive().isThreeColumn();
  5859. const auto topShadowLeft = (isOneColumn || _inGrab)
  5860. ? 0
  5861. : st::lineWidth;
  5862. const auto topShadowRight = (isThreeColumn && !_inGrab && _peer)
  5863. ? st::lineWidth
  5864. : 0;
  5865. _topShadow->setGeometryToLeft(
  5866. topShadowLeft,
  5867. _topBar->bottomNoMargins(),
  5868. width() - topShadowLeft - topShadowRight,
  5869. st::lineWidth);
  5870. }
  5871. void HistoryWidget::itemRemoved(not_null<const HistoryItem*> item) {
  5872. if (item == _replyEditMsg && _editMsgId) {
  5873. cancelEdit();
  5874. }
  5875. if (item == _replyEditMsg && _replyTo) {
  5876. cancelReply();
  5877. }
  5878. if (item == _processingReplyItem) {
  5879. _processingReplyTo = {};
  5880. _processingReplyItem = nullptr;
  5881. }
  5882. if (_kbReplyTo && item == _kbReplyTo) {
  5883. toggleKeyboard();
  5884. _kbReplyTo = nullptr;
  5885. }
  5886. const auto i = _itemRevealAnimations.find(item);
  5887. if (i != end(_itemRevealAnimations)) {
  5888. _itemRevealAnimations.erase(i);
  5889. revealItemsCallback();
  5890. }
  5891. const auto j = _itemRevealPending.find(item);
  5892. if (j != _itemRevealPending.end()) {
  5893. _itemRevealPending.erase(j);
  5894. }
  5895. }
  5896. void HistoryWidget::itemEdited(not_null<HistoryItem*> item) {
  5897. if (item.get() == _replyEditMsg) {
  5898. updateReplyEditTexts(true);
  5899. }
  5900. }
  5901. FullReplyTo HistoryWidget::replyTo() const {
  5902. return _replyTo
  5903. ? _replyTo
  5904. : _kbReplyTo
  5905. ? FullReplyTo{ _kbReplyTo->fullId() }
  5906. : (_peer && _peer->forum())
  5907. ? FullReplyTo{ .topicRootId = Data::ForumTopic::kGeneralId }
  5908. : FullReplyTo();
  5909. }
  5910. bool HistoryWidget::hasSavedScroll() const {
  5911. Expects(_history != nullptr);
  5912. return _history->scrollTopItem
  5913. || (_migrated && _migrated->scrollTopItem);
  5914. }
  5915. int HistoryWidget::countInitialScrollTop() {
  5916. if (hasSavedScroll()) {
  5917. return _list->historyScrollTop();
  5918. } else if (_showAtMsgId
  5919. && (IsServerMsgId(_showAtMsgId)
  5920. || IsClientMsgId(_showAtMsgId)
  5921. || IsServerMsgId(-_showAtMsgId))) {
  5922. const auto item = getItemFromHistoryOrMigrated(_showAtMsgId);
  5923. const auto itemTop = _list->itemTop(item);
  5924. if (itemTop < 0) {
  5925. setMsgId(ShowAtUnreadMsgId);
  5926. controller()->showToast(tr::lng_message_not_found(tr::now));
  5927. return countInitialScrollTop();
  5928. } else {
  5929. const auto view = item->mainView();
  5930. Assert(view != nullptr);
  5931. enqueueMessageHighlight({
  5932. item,
  5933. base::take(_showAtMsgParams.highlightPart),
  5934. base::take(_showAtMsgParams.highlightPartOffsetHint),
  5935. });
  5936. const auto result = itemTopForHighlight(view);
  5937. createUnreadBarIfBelowVisibleArea(result);
  5938. return result;
  5939. }
  5940. } else if (_showAtMsgId == ShowAtTheEndMsgId) {
  5941. return ScrollMax;
  5942. } else if (const auto top = unreadBarTop()) {
  5943. return *top;
  5944. } else {
  5945. _history->calculateFirstUnreadMessage();
  5946. return countAutomaticScrollTop();
  5947. }
  5948. }
  5949. void HistoryWidget::createUnreadBarIfBelowVisibleArea(int withScrollTop) {
  5950. Expects(_history != nullptr);
  5951. if (_history->unreadBar()) {
  5952. return;
  5953. }
  5954. _history->calculateFirstUnreadMessage();
  5955. if (const auto unread = _history->firstUnreadMessage()) {
  5956. if (_list->itemTop(unread) > withScrollTop) {
  5957. createUnreadBarAndResize();
  5958. }
  5959. }
  5960. }
  5961. void HistoryWidget::createUnreadBarAndResize() {
  5962. if (!_history->firstUnreadMessage()) {
  5963. return;
  5964. }
  5965. const auto was = base::take(_historyInited);
  5966. _history->addUnreadBar();
  5967. if (hasPendingResizedItems()) {
  5968. updateListSize();
  5969. }
  5970. _historyInited = was;
  5971. }
  5972. int HistoryWidget::countAutomaticScrollTop() {
  5973. Expects(_history != nullptr);
  5974. Expects(_list != nullptr);
  5975. if (const auto unread = _history->firstUnreadMessage()) {
  5976. const auto firstUnreadTop = _list->itemTop(unread);
  5977. const auto possibleUnreadBarTop = _scroll->scrollTopMax()
  5978. + HistoryView::UnreadBar::height()
  5979. - HistoryView::UnreadBar::marginTop();
  5980. if (firstUnreadTop < possibleUnreadBarTop) {
  5981. createUnreadBarAndResize();
  5982. if (_history->unreadBar() != nullptr) {
  5983. setMsgId(ShowAtUnreadMsgId);
  5984. return countInitialScrollTop();
  5985. }
  5986. }
  5987. }
  5988. return ScrollMax;
  5989. }
  5990. Data::SendError HistoryWidget::computeSendRestriction() const {
  5991. const auto allWithoutPolls = Data::AllSendRestrictions()
  5992. & ~ChatRestriction::SendPolls;
  5993. return (_peer && !Data::CanSendAnyOf(_peer, allWithoutPolls))
  5994. ? Data::RestrictionError(_peer, ChatRestriction::SendOther)
  5995. : Data::SendError();
  5996. }
  5997. void HistoryWidget::updateSendRestriction() {
  5998. const auto restriction = computeSendRestriction();
  5999. if (_sendRestrictionKey == restriction.text) {
  6000. return;
  6001. }
  6002. _sendRestrictionKey = restriction.text;
  6003. if (!restriction) {
  6004. _sendRestriction = nullptr;
  6005. } else if (restriction.premiumToLift) {
  6006. _sendRestriction = PremiumRequiredSendRestriction(
  6007. this,
  6008. _peer->asUser(),
  6009. controller());
  6010. } else if (const auto lifting = restriction.boostsToLift) {
  6011. auto button = base::make_unique_q<Ui::FlatButton>(
  6012. this,
  6013. restriction.text,
  6014. st::historyComposeButton);
  6015. const auto channel = _peer->asChannel();
  6016. button->setClickedCallback([=] {
  6017. controller()->resolveBoostState(channel, lifting);
  6018. });
  6019. _sendRestriction = std::move(button);
  6020. } else {
  6021. _sendRestriction = TextErrorSendRestriction(this, restriction.text);
  6022. }
  6023. if (_sendRestriction) {
  6024. _sendRestriction->show();
  6025. moveFieldControls();
  6026. }
  6027. }
  6028. void HistoryWidget::updateHistoryGeometry(
  6029. bool initial,
  6030. bool loadedDown,
  6031. const ScrollChange &change) {
  6032. const auto guard = gsl::finally([&] {
  6033. _itemRevealPending.clear();
  6034. });
  6035. if (!_history
  6036. || (initial && _historyInited)
  6037. || (!initial && !_historyInited)) {
  6038. return;
  6039. }
  6040. if (_firstLoadRequest || _showAnimation) {
  6041. _updateHistoryGeometryRequired = true;
  6042. // scrollTopMax etc are not working after recountHistoryGeometry()
  6043. return;
  6044. }
  6045. auto newScrollHeight = height() - _topBar->height();
  6046. if (_translateBar) {
  6047. newScrollHeight -= _translateBar->height();
  6048. }
  6049. if (_sponsoredMessageBar) {
  6050. newScrollHeight -= _sponsoredMessageBar->height();
  6051. }
  6052. if (_pinnedBar) {
  6053. newScrollHeight -= _pinnedBar->height();
  6054. }
  6055. if (_groupCallBar) {
  6056. newScrollHeight -= _groupCallBar->height();
  6057. }
  6058. if (_requestsBar) {
  6059. newScrollHeight -= _requestsBar->height();
  6060. }
  6061. if (_paysStatus) {
  6062. newScrollHeight -= _paysStatus->bar().height();
  6063. }
  6064. if (_contactStatus) {
  6065. newScrollHeight -= _contactStatus->bar().height();
  6066. }
  6067. if (_businessBotStatus) {
  6068. newScrollHeight -= _businessBotStatus->bar().height();
  6069. }
  6070. if (isChoosingTheme()) {
  6071. newScrollHeight -= _chooseTheme->height();
  6072. } else if (!editingMessage()
  6073. && (isSearching()
  6074. || isBlocked()
  6075. || isBotStart()
  6076. || isJoinChannel()
  6077. || isMuteUnmute()
  6078. || isReportMessages())) {
  6079. newScrollHeight -= _unblock->height();
  6080. } else {
  6081. if (editingMessage() || _canSendMessages) {
  6082. newScrollHeight -= (fieldHeight() + 2 * st::historySendPadding);
  6083. } else if (_sendRestriction) {
  6084. newScrollHeight -= _sendRestriction->height();
  6085. }
  6086. if (_editMsgId
  6087. || replyTo()
  6088. || readyToForward()
  6089. || _previewDrawPreview) {
  6090. newScrollHeight -= st::historyReplyHeight;
  6091. }
  6092. if (_kbShown) {
  6093. newScrollHeight -= _kbScroll->height();
  6094. }
  6095. }
  6096. if (newScrollHeight <= 0) {
  6097. return;
  6098. }
  6099. const auto wasScrollTop = _scroll->scrollTop();
  6100. const auto wasAtBottom = (wasScrollTop == _scroll->scrollTopMax());
  6101. const auto needResize = (_scroll->width() != width())
  6102. || (_scroll->height() != newScrollHeight);
  6103. if (needResize) {
  6104. _scroll->resize(width(), newScrollHeight);
  6105. // on initial updateListSize we didn't put the _scroll->scrollTop
  6106. // correctly yet so visibleAreaUpdated() call will erase it
  6107. // with the new (undefined) value
  6108. if (!initial) {
  6109. visibleAreaUpdated();
  6110. }
  6111. }
  6112. if (needResize || initial) {
  6113. if (_autocomplete) {
  6114. _autocomplete->setBoundings(_scroll->geometry());
  6115. }
  6116. if (_supportAutocomplete) {
  6117. _supportAutocomplete->setBoundings(_scroll->geometry());
  6118. }
  6119. _cornerButtons.updatePositions();
  6120. controller()->floatPlayerAreaUpdated();
  6121. }
  6122. updateListSize();
  6123. _updateHistoryGeometryRequired = false;
  6124. auto newScrollTop = 0;
  6125. if (initial) {
  6126. newScrollTop = countInitialScrollTop();
  6127. _historyInited = true;
  6128. _scrollToAnimation.stop();
  6129. } else if (wasAtBottom && !loadedDown && !_history->unreadBar()) {
  6130. newScrollTop = countAutomaticScrollTop();
  6131. } else {
  6132. newScrollTop = std::min(
  6133. _list->historyScrollTop(),
  6134. _scroll->scrollTopMax());
  6135. if (change.type == ScrollChangeAdd) {
  6136. newScrollTop += change.value;
  6137. } else if (change.type == ScrollChangeNoJumpToBottom) {
  6138. newScrollTop = wasScrollTop;
  6139. }
  6140. }
  6141. const auto toY = std::clamp(newScrollTop, 0, _scroll->scrollTopMax());
  6142. synteticScrollToY(toY);
  6143. if (initial && _showAtMsgId) {
  6144. const auto timestamp = base::take(_showAtMsgParams.videoTimestamp);
  6145. if (timestamp.has_value()) {
  6146. const auto item = session().data().message(_peer, _showAtMsgId);
  6147. const auto media = item ? item->media() : nullptr;
  6148. const auto document = media ? media->document() : nullptr;
  6149. if (document && document->isVideoFile()) {
  6150. controller()->openDocument(
  6151. document,
  6152. true,
  6153. { item->fullId() },
  6154. nullptr,
  6155. timestamp);
  6156. }
  6157. }
  6158. }
  6159. }
  6160. void HistoryWidget::revealItemsCallback() {
  6161. auto height = 0;
  6162. if (!_historyInited) {
  6163. _itemRevealAnimations.clear();
  6164. }
  6165. for (auto i = begin(_itemRevealAnimations)
  6166. ; i != end(_itemRevealAnimations);) {
  6167. if (!i->second.animation.animating()) {
  6168. i = _itemRevealAnimations.erase(i);
  6169. } else {
  6170. height += anim::interpolate(
  6171. i->second.startHeight,
  6172. 0,
  6173. i->second.animation.value(1.));
  6174. ++i;
  6175. }
  6176. }
  6177. if (_itemsRevealHeight != height) {
  6178. const auto wasScrollTop = _scroll->scrollTop();
  6179. const auto wasAtBottom = (wasScrollTop == _scroll->scrollTopMax());
  6180. if (!wasAtBottom) {
  6181. height = 0;
  6182. _itemRevealAnimations.clear();
  6183. }
  6184. _itemsRevealHeight = height;
  6185. _list->changeItemsRevealHeight(_itemsRevealHeight);
  6186. const auto newScrollTop = (wasAtBottom && !_history->unreadBar())
  6187. ? countAutomaticScrollTop()
  6188. : _list->historyScrollTop();
  6189. const auto toY = std::clamp(newScrollTop, 0, _scroll->scrollTopMax());
  6190. synteticScrollToY(toY);
  6191. }
  6192. }
  6193. void HistoryWidget::startItemRevealAnimations() {
  6194. for (const auto &item : base::take(_itemRevealPending)) {
  6195. if (const auto view = item->mainView()) {
  6196. if (const auto top = _list->itemTop(view); top >= 0) {
  6197. if (const auto height = view->height()) {
  6198. startMessageSendingAnimation(item);
  6199. if (!_itemRevealAnimations.contains(item)) {
  6200. auto &animation = _itemRevealAnimations[item];
  6201. animation.startHeight = height;
  6202. _itemsRevealHeight += height;
  6203. animation.animation.start(
  6204. [=] { revealItemsCallback(); },
  6205. 0.,
  6206. 1.,
  6207. HistoryView::ListWidget::kItemRevealDuration,
  6208. anim::easeOutCirc);
  6209. if (item->out() || _history->peer->isSelf()) {
  6210. _list->theme()->rotateComplexGradientBackground();
  6211. }
  6212. }
  6213. }
  6214. }
  6215. }
  6216. }
  6217. }
  6218. void HistoryWidget::startMessageSendingAnimation(
  6219. not_null<HistoryItem*> item) {
  6220. auto &sendingAnimation = controller()->sendingAnimation();
  6221. if (!sendingAnimation.checkExpectedType(item)) {
  6222. return;
  6223. }
  6224. Assert(item->mainView() != nullptr);
  6225. Assert(item->mainView()->media() != nullptr);
  6226. auto globalEndTopLeft = rpl::merge(
  6227. _scroll->innerResizes() | rpl::to_empty,
  6228. session().data().newItemAdded() | rpl::to_empty,
  6229. geometryValue() | rpl::to_empty,
  6230. _scroll->geometryValue() | rpl::to_empty,
  6231. _list->geometryValue() | rpl::to_empty
  6232. ) | rpl::map([=]() -> std::optional<QPoint> {
  6233. const auto view = item->mainView();
  6234. const auto top = view ? _list->itemTop(view) : -1;
  6235. if (top < 0) {
  6236. return std::nullopt;
  6237. }
  6238. const auto additional = (_list->height() == _scroll->height())
  6239. ? view->height()
  6240. : 0;
  6241. return _list->mapToGlobal(QPoint(0, top - additional));
  6242. });
  6243. sendingAnimation.startAnimation({
  6244. .globalEndTopLeft = std::move(globalEndTopLeft),
  6245. .view = [=] { return item->mainView(); },
  6246. .paintContext = [=] { return _list->preparePaintContext({}); },
  6247. });
  6248. }
  6249. void HistoryWidget::updateListSize() {
  6250. Expects(_list != nullptr);
  6251. _list->recountHistoryGeometry(!_historyInited);
  6252. auto washidden = _scroll->isHidden();
  6253. if (washidden) {
  6254. _scroll->show();
  6255. }
  6256. startItemRevealAnimations();
  6257. _list->setItemsRevealHeight(_itemsRevealHeight);
  6258. _list->updateSize();
  6259. if (washidden) {
  6260. _scroll->hide();
  6261. }
  6262. _updateHistoryGeometryRequired = true;
  6263. }
  6264. bool HistoryWidget::hasPendingResizedItems() const {
  6265. if (!_list) {
  6266. // Based on the crash reports there is a codepath (at least on macOS)
  6267. // that leads from _list = _scroll->setOwnedWidget(...) right into
  6268. // the HistoryWidget::paintEvent (by sending fake mouse move events
  6269. // inside scroll area -> hiding tooltip window -> exposing the main
  6270. // window -> syncing it backing store synchronously).
  6271. //
  6272. // So really we could get here with !_list && (_history != nullptr).
  6273. return false;
  6274. }
  6275. return (_history && _history->hasPendingResizedItems())
  6276. || (_migrated && _migrated->hasPendingResizedItems());
  6277. }
  6278. std::optional<int> HistoryWidget::unreadBarTop() const {
  6279. const auto bar = [&]() -> HistoryView::Element* {
  6280. if (const auto bar = _migrated ? _migrated->unreadBar() : nullptr) {
  6281. return bar;
  6282. }
  6283. return _history->unreadBar();
  6284. }();
  6285. if (bar) {
  6286. const auto result = _list->itemTop(bar)
  6287. + HistoryView::UnreadBar::marginTop();
  6288. if (bar->Has<HistoryView::DateBadge>()) {
  6289. return result + bar->Get<HistoryView::DateBadge>()->height();
  6290. }
  6291. return result;
  6292. }
  6293. return std::nullopt;
  6294. }
  6295. void HistoryWidget::addMessagesToFront(
  6296. not_null<PeerData*> peer,
  6297. const QVector<MTPMessage> &messages) {
  6298. _list->messagesReceived(peer, messages);
  6299. if (!_firstLoadRequest) {
  6300. updateHistoryGeometry();
  6301. updateBotKeyboard();
  6302. }
  6303. }
  6304. void HistoryWidget::addMessagesToBack(
  6305. not_null<PeerData*> peer,
  6306. const QVector<MTPMessage> &messages) {
  6307. const auto checkForUnreadStart = [&] {
  6308. if (_history->unreadBar() || !_history->trackUnreadMessages()) {
  6309. return false;
  6310. }
  6311. _history->calculateFirstUnreadMessage();
  6312. return !_history->firstUnreadMessage();
  6313. }();
  6314. _list->messagesReceivedDown(peer, messages);
  6315. if (checkForUnreadStart) {
  6316. _history->calculateFirstUnreadMessage();
  6317. createUnreadBarAndResize();
  6318. }
  6319. if (!_firstLoadRequest) {
  6320. updateHistoryGeometry(false, true, { ScrollChangeNoJumpToBottom, 0 });
  6321. }
  6322. injectSponsoredMessages();
  6323. }
  6324. void HistoryWidget::updateBotKeyboard(History *h, bool force) {
  6325. if (h && h != _history && h != _migrated) {
  6326. return;
  6327. }
  6328. const auto wasVisible = _kbShown || _kbReplyTo;
  6329. const auto wasMsgId = _keyboard->forMsgId();
  6330. auto changed = false;
  6331. if ((_replyTo && !_replyEditMsg) || _editMsgId || !_history) {
  6332. changed = _keyboard->updateMarkup(nullptr, force);
  6333. } else if (_replyTo && _replyEditMsg) {
  6334. changed = _keyboard->updateMarkup(_replyEditMsg, force);
  6335. } else {
  6336. const auto keyboardItem = _history->lastKeyboardId
  6337. ? session().data().message(
  6338. _history->peer,
  6339. _history->lastKeyboardId)
  6340. : nullptr;
  6341. changed = _keyboard->updateMarkup(keyboardItem, force);
  6342. }
  6343. const auto controlsChanged = updateCmdStartShown();
  6344. if (!changed) {
  6345. if (controlsChanged) {
  6346. updateControlsGeometry();
  6347. }
  6348. return;
  6349. } else if (_keyboard->forMsgId() != wasMsgId) {
  6350. _kbScroll->scrollTo({ 0, 0 });
  6351. }
  6352. const auto hasMarkup = _keyboard->hasMarkup();
  6353. const auto forceReply = _keyboard->forceReply()
  6354. && (!_replyTo || !_replyEditMsg);
  6355. if (hasMarkup || forceReply) {
  6356. if (_keyboard->singleUse()
  6357. && _keyboard->hasMarkup()
  6358. && (_keyboard->forMsgId()
  6359. == FullMsgId(_history->peer->id, _history->lastKeyboardId))
  6360. && _history->lastKeyboardUsed) {
  6361. _history->lastKeyboardHiddenId = _history->lastKeyboardId;
  6362. }
  6363. if (!isSearching()
  6364. && !isBotStart()
  6365. && !isBlocked()
  6366. && _canSendMessages
  6367. && (wasVisible
  6368. || (_replyTo && _replyEditMsg)
  6369. || (!HasSendText(_field) && !kbWasHidden()))) {
  6370. if (!_showAnimation) {
  6371. if (hasMarkup) {
  6372. _kbScroll->show();
  6373. _tabbedSelectorToggle->hide();
  6374. showKeyboardHideButton();
  6375. } else {
  6376. _kbScroll->hide();
  6377. _tabbedSelectorToggle->show();
  6378. _botKeyboardHide->hide();
  6379. }
  6380. _botKeyboardShow->hide();
  6381. _botCommandStart->hide();
  6382. }
  6383. const auto maxheight = computeMaxFieldHeight();
  6384. const auto kbheight = hasMarkup
  6385. ? qMin(_keyboard->height(), maxheight - (maxheight / 2))
  6386. : 0;
  6387. _field->setMaxHeight(maxheight - kbheight);
  6388. _kbShown = hasMarkup;
  6389. _kbReplyTo = (_peer->isChat()
  6390. || _peer->isChannel()
  6391. || _keyboard->forceReply())
  6392. ? session().data().message(_keyboard->forMsgId())
  6393. : nullptr;
  6394. if (_kbReplyTo && !_replyTo) {
  6395. updateReplyToName();
  6396. updateReplyEditText(_kbReplyTo);
  6397. }
  6398. } else {
  6399. if (!_showAnimation) {
  6400. _kbScroll->hide();
  6401. _tabbedSelectorToggle->show();
  6402. _botKeyboardHide->hide();
  6403. _botKeyboardShow->show();
  6404. _botCommandStart->hide();
  6405. }
  6406. _field->setMaxHeight(computeMaxFieldHeight());
  6407. _kbShown = false;
  6408. _kbReplyTo = nullptr;
  6409. if (!readyToForward()
  6410. && !_previewDrawPreview
  6411. && !_replyTo) {
  6412. _fieldBarCancel->hide();
  6413. updateMouseTracking();
  6414. }
  6415. }
  6416. } else {
  6417. if (!_scroll->isHidden()) {
  6418. _kbScroll->hide();
  6419. _tabbedSelectorToggle->show();
  6420. _botKeyboardHide->hide();
  6421. _botKeyboardShow->hide();
  6422. _botCommandStart->setVisible(!_editMsgId);
  6423. }
  6424. _field->setMaxHeight(computeMaxFieldHeight());
  6425. _kbShown = false;
  6426. _kbReplyTo = nullptr;
  6427. if (!readyToForward()
  6428. && !_previewDrawPreview
  6429. && !_replyTo
  6430. && !_editMsgId) {
  6431. _fieldBarCancel->hide();
  6432. updateMouseTracking();
  6433. }
  6434. }
  6435. refreshTopBarActiveChat();
  6436. updateFieldPlaceholder();
  6437. updateControlsGeometry();
  6438. update();
  6439. }
  6440. void HistoryWidget::botCallbackSent(not_null<HistoryItem*> item) {
  6441. if (!item->isRegular() || _peer != item->history()->peer) {
  6442. return;
  6443. }
  6444. const auto keyId = _keyboard->forMsgId();
  6445. const auto lastKeyboardUsed = (keyId == FullMsgId(_peer->id, item->id))
  6446. && (keyId == FullMsgId(_peer->id, _history->lastKeyboardId));
  6447. session().data().requestItemRepaint(item);
  6448. if (_replyTo.messageId == item->fullId()) {
  6449. cancelReply();
  6450. }
  6451. if (_keyboard->singleUse()
  6452. && _keyboard->hasMarkup()
  6453. && lastKeyboardUsed) {
  6454. if (_kbShown) {
  6455. toggleKeyboard(false);
  6456. }
  6457. _history->lastKeyboardUsed = true;
  6458. }
  6459. }
  6460. int HistoryWidget::computeMaxFieldHeight() const {
  6461. const auto available = height()
  6462. - _topBar->height()
  6463. - (_paysStatus ? _paysStatus->bar().height() : 0)
  6464. - (_contactStatus ? _contactStatus->bar().height() : 0)
  6465. - (_businessBotStatus ? _businessBotStatus->bar().height() : 0)
  6466. - (_sponsoredMessageBar ? _sponsoredMessageBar->height() : 0)
  6467. - (_pinnedBar ? _pinnedBar->height() : 0)
  6468. - (_groupCallBar ? _groupCallBar->height() : 0)
  6469. - (_requestsBar ? _requestsBar->height() : 0)
  6470. - ((_editMsgId
  6471. || replyTo()
  6472. || readyToForward()
  6473. || _previewDrawPreview)
  6474. ? st::historyReplyHeight
  6475. : 0)
  6476. - (2 * st::historySendPadding)
  6477. - st::historyReplyHeight; // at least this height for history.
  6478. return std::min(st::historyComposeFieldMaxHeight, available);
  6479. }
  6480. bool HistoryWidget::cornerButtonsIgnoreVisibility() {
  6481. return _showAnimation != nullptr;
  6482. }
  6483. std::optional<bool> HistoryWidget::cornerButtonsDownShown() {
  6484. if (!_list || _firstLoadRequest) {
  6485. return false;
  6486. }
  6487. if (_voiceRecordBar->isLockPresent()
  6488. || _voiceRecordBar->isTTLButtonShown()) {
  6489. return false;
  6490. }
  6491. if (!_history->loadedAtBottom() || _cornerButtons.replyReturn()) {
  6492. return true;
  6493. }
  6494. const auto top = _scroll->scrollTop() + st::historyToDownShownAfter;
  6495. if (top < _scroll->scrollTopMax()) {
  6496. return true;
  6497. }
  6498. const auto haveUnreadBelowBottom = [&](History *history) {
  6499. if (!_list
  6500. || !history
  6501. || history->unreadCount() <= 0
  6502. || !history->trackUnreadMessages()) {
  6503. return false;
  6504. }
  6505. const auto unread = history->firstUnreadMessage();
  6506. if (!unread) {
  6507. return false;
  6508. }
  6509. const auto top = _list->itemTop(unread);
  6510. return (top >= _scroll->scrollTop() + _scroll->height());
  6511. };
  6512. if (haveUnreadBelowBottom(_history)
  6513. || haveUnreadBelowBottom(_migrated)) {
  6514. return true;
  6515. }
  6516. return false;
  6517. }
  6518. bool HistoryWidget::cornerButtonsUnreadMayBeShown() {
  6519. return !_firstLoadRequest && !_voiceRecordBar->isLockPresent();
  6520. }
  6521. bool HistoryWidget::cornerButtonsHas(HistoryView::CornerButtonType type) {
  6522. return true;
  6523. }
  6524. void HistoryWidget::mousePressEvent(QMouseEvent *e) {
  6525. if (!_list) {
  6526. // Remove focus from the chats list search.
  6527. setFocus();
  6528. // Set it back to the chats list so that typing filter chats.
  6529. controller()->widget()->setInnerFocus();
  6530. return;
  6531. }
  6532. const auto isReadyToForward = readyToForward();
  6533. if (_editMsgId
  6534. && (_inDetails || _inPhotoEdit)
  6535. && (e->button() == Qt::RightButton)) {
  6536. _mediaEditManager.showMenu(
  6537. _list,
  6538. [=] { mouseMoveEvent(nullptr); },
  6539. HasSendText(_field));
  6540. } else if (_inPhotoEdit && _photoEditMedia) {
  6541. EditCaptionBox::StartPhotoEdit(
  6542. controller(),
  6543. _photoEditMedia,
  6544. { _history->peer->id, _editMsgId },
  6545. _field->getTextWithTags(),
  6546. _mediaEditManager.spoilered(),
  6547. _mediaEditManager.invertCaption(),
  6548. crl::guard(_list, [=] { cancelEdit(); }));
  6549. } else if (!_inDetails) {
  6550. return;
  6551. } else if (_previewDrawPreview) {
  6552. editDraftOptions();
  6553. } else if (_editMsgId) {
  6554. controller()->showPeerHistory(
  6555. _peer,
  6556. Window::SectionShow::Way::Forward,
  6557. _editMsgId);
  6558. } else if (_replyTo
  6559. && ((e->modifiers() & Qt::ControlModifier)
  6560. || (e->button() != Qt::LeftButton))) {
  6561. jumpToReply(_replyTo);
  6562. } else if (_replyTo
  6563. || (isReadyToForward && e->button() == Qt::LeftButton)) {
  6564. editDraftOptions();
  6565. } else if (isReadyToForward) {
  6566. _forwardPanel->editToNextOption();
  6567. } else if (_kbReplyTo) {
  6568. controller()->showPeerHistory(
  6569. _kbReplyTo->history()->peer->id,
  6570. Window::SectionShow::Way::Forward,
  6571. _kbReplyTo->id);
  6572. }
  6573. }
  6574. void HistoryWidget::editDraftOptions() {
  6575. Expects(_history != nullptr);
  6576. const auto history = _history;
  6577. const auto reply = _replyTo;
  6578. const auto webpage = _preview->draft();
  6579. const auto forward = _forwardPanel->draft();
  6580. const auto done = [=](
  6581. FullReplyTo replyTo,
  6582. Data::WebPageDraft webpage,
  6583. Data::ForwardDraft forward) {
  6584. if (replyTo) {
  6585. replyToMessage(replyTo);
  6586. } else {
  6587. cancelReply();
  6588. }
  6589. history->setForwardDraft({}, std::move(forward));
  6590. _preview->apply(webpage);
  6591. };
  6592. const auto replyToId = reply.messageId;
  6593. const auto highlight = crl::guard(this, [=](FullReplyTo to) {
  6594. jumpToReply(to);
  6595. });
  6596. using namespace HistoryView::Controls;
  6597. EditDraftOptions({
  6598. .show = controller()->uiShow(),
  6599. .history = history,
  6600. .draft = Data::Draft(_field, reply, _preview->draft()),
  6601. .usedLink = _preview->link(),
  6602. .forward = _forwardPanel->draft(),
  6603. .links = _preview->links(),
  6604. .resolver = _preview->resolver(),
  6605. .done = done,
  6606. .highlight = highlight,
  6607. .clearOldDraft = [=] { ClearDraftReplyTo(history, 0, replyToId); },
  6608. });
  6609. }
  6610. void HistoryWidget::jumpToReply(FullReplyTo to) {
  6611. if (const auto item = session().data().message(to.messageId)) {
  6612. JumpToMessageClickHandler(
  6613. item,
  6614. {},
  6615. to.quote,
  6616. to.quoteOffset
  6617. )->onClick({});
  6618. }
  6619. }
  6620. void HistoryWidget::keyPressEvent(QKeyEvent *e) {
  6621. if (!_history) return;
  6622. const auto commonModifiers = e->modifiers() & kCommonModifiers;
  6623. if (e->key() == Qt::Key_Escape) {
  6624. if (hasFocus()) {
  6625. escape();
  6626. } else {
  6627. e->ignore();
  6628. }
  6629. } else if (e->key() == Qt::Key_Back) {
  6630. _cancelRequests.fire({});
  6631. } else if (e->key() == Qt::Key_PageDown) {
  6632. _scroll->keyPressEvent(e);
  6633. } else if (e->key() == Qt::Key_PageUp) {
  6634. _scroll->keyPressEvent(e);
  6635. } else if (e->key() == Qt::Key_Down && !commonModifiers) {
  6636. _scroll->keyPressEvent(e);
  6637. } else if (e->key() == Qt::Key_Up && !commonModifiers) {
  6638. const auto item = _history
  6639. ? _history->lastEditableMessage()
  6640. : nullptr;
  6641. if (item
  6642. && _field->empty()
  6643. && !_editMsgId
  6644. && !_replyTo) {
  6645. editMessage(item, {});
  6646. return;
  6647. }
  6648. _scroll->keyPressEvent(e);
  6649. } else if (e->key() == Qt::Key_Up
  6650. && commonModifiers == Qt::ControlModifier) {
  6651. if (!replyToPreviousMessage()) {
  6652. e->ignore();
  6653. }
  6654. } else if (e->key() == Qt::Key_Down
  6655. && commonModifiers == Qt::ControlModifier) {
  6656. if (!replyToNextMessage()) {
  6657. e->ignore();
  6658. }
  6659. } else if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) {
  6660. if (!_botStart->isHidden()) {
  6661. sendBotStartCommand();
  6662. }
  6663. if (!_canSendMessages) {
  6664. const auto submitting = Ui::InputField::ShouldSubmit(
  6665. Core::App().settings().sendSubmitWay(),
  6666. e->modifiers());
  6667. if (submitting) {
  6668. sendWithModifiers(e->modifiers());
  6669. }
  6670. }
  6671. } else if ((e->key() == Qt::Key_O)
  6672. && (e->modifiers() == Qt::ControlModifier)) {
  6673. chooseAttach();
  6674. } else {
  6675. e->ignore();
  6676. }
  6677. }
  6678. void HistoryWidget::handlePeerMigration() {
  6679. const auto current = _peer->migrateToOrMe();
  6680. const auto chat = current->migrateFrom();
  6681. if (!chat) {
  6682. return;
  6683. }
  6684. const auto channel = current->asChannel();
  6685. Assert(channel != nullptr);
  6686. if (_peer != channel) {
  6687. showHistory(
  6688. channel->id,
  6689. (_showAtMsgId > 0) ? (-_showAtMsgId) : _showAtMsgId);
  6690. channel->session().api().chatParticipants().requestCountDelayed(
  6691. channel);
  6692. } else {
  6693. _migrated = _history->migrateFrom();
  6694. _list->notifyMigrateUpdated();
  6695. setupPinnedTracker();
  6696. setupGroupCallBar();
  6697. setupRequestsBar();
  6698. updateHistoryGeometry();
  6699. }
  6700. const auto from = chat->owner().historyLoaded(chat);
  6701. const auto to = channel->owner().historyLoaded(channel);
  6702. if (from
  6703. && to
  6704. && !from->isEmpty()
  6705. && (!from->loadedAtBottom() || !to->loadedAtTop())) {
  6706. from->clear(History::ClearType::Unload);
  6707. }
  6708. }
  6709. bool HistoryWidget::replyToPreviousMessage() {
  6710. if (!_history
  6711. || _editMsgId
  6712. || _history->isForum()
  6713. || (_replyTo && _replyTo.messageId.peer != _history->peer->id)) {
  6714. return false;
  6715. }
  6716. const auto fullId = FullMsgId(
  6717. _history->peer->id,
  6718. (_field->isVisible()
  6719. ? _replyTo.messageId.msg
  6720. : _highlighter.latestSingleHighlightedMsgId()));
  6721. if (const auto item = session().data().message(fullId)) {
  6722. if (const auto view = item->mainView()) {
  6723. if (const auto previousView = view->previousDisplayedInBlocks()) {
  6724. const auto previous = previousView->data();
  6725. controller()->showMessage(previous);
  6726. if (_field->isVisible()) {
  6727. replyToMessage(previous);
  6728. }
  6729. return true;
  6730. }
  6731. }
  6732. } else if (const auto previousView = _history->findLastDisplayed()) {
  6733. const auto previous = previousView->data();
  6734. controller()->showMessage(previous);
  6735. if (_field->isVisible()) {
  6736. replyToMessage(previous);
  6737. }
  6738. return true;
  6739. }
  6740. return false;
  6741. }
  6742. bool HistoryWidget::replyToNextMessage() {
  6743. if (!_history
  6744. || _editMsgId
  6745. || _history->isForum()
  6746. || (_replyTo && _replyTo.messageId.peer != _history->peer->id)) {
  6747. return false;
  6748. }
  6749. const auto fullId = FullMsgId(
  6750. _history->peer->id,
  6751. (_field->isVisible()
  6752. ? _replyTo.messageId.msg
  6753. : _highlighter.latestSingleHighlightedMsgId()));
  6754. if (const auto item = session().data().message(fullId)) {
  6755. if (const auto view = item->mainView()) {
  6756. if (const auto nextView = view->nextDisplayedInBlocks()) {
  6757. const auto next = nextView->data();
  6758. controller()->showMessage(next);
  6759. if (_field->isVisible()) {
  6760. replyToMessage(next);
  6761. }
  6762. } else {
  6763. _highlighter.clear();
  6764. cancelReply(false);
  6765. }
  6766. return true;
  6767. }
  6768. }
  6769. return false;
  6770. }
  6771. bool HistoryWidget::showSlowmodeError() {
  6772. const auto text = [&] {
  6773. if (const auto left = _peer->slowmodeSecondsLeft()) {
  6774. return tr::lng_slowmode_enabled(
  6775. tr::now,
  6776. lt_left,
  6777. Ui::FormatDurationWordsSlowmode(left));
  6778. } else if (_peer->slowmodeApplied()) {
  6779. if (const auto item = _history->latestSendingMessage()) {
  6780. if (const auto view = item->mainView()) {
  6781. animatedScrollToItem(item->id);
  6782. enqueueMessageHighlight({ item });
  6783. }
  6784. return tr::lng_slowmode_no_many(tr::now);
  6785. }
  6786. }
  6787. return QString();
  6788. }();
  6789. if (text.isEmpty()) {
  6790. return false;
  6791. }
  6792. controller()->showToast(text);
  6793. return true;
  6794. }
  6795. void HistoryWidget::fieldTabbed() {
  6796. if (_supportAutocomplete) {
  6797. _supportAutocomplete->activate(_field.data());
  6798. }
  6799. }
  6800. void HistoryWidget::sendInlineResult(InlineBots::ResultSelected result) {
  6801. if (!_peer || !_canSendMessages) {
  6802. return;
  6803. } else if (showSlowmodeError()) {
  6804. return;
  6805. } else if (const auto error = result.result->getErrorOnSend(_history)) {
  6806. Data::ShowSendErrorToast(controller(), _peer, error);
  6807. return;
  6808. }
  6809. const auto withPaymentApproved = [=](int approved) {
  6810. auto copy = result;
  6811. copy.options.starsApproved = approved;
  6812. sendInlineResult(copy);
  6813. };
  6814. const auto checked = checkSendPayment(
  6815. 1,
  6816. result.options.starsApproved,
  6817. withPaymentApproved);
  6818. if (!checked) {
  6819. return;
  6820. }
  6821. controller()->sendingAnimation().appendSending(
  6822. result.messageSendingFrom);
  6823. auto action = prepareSendAction(result.options);
  6824. action.generateLocal = true;
  6825. session().api().sendInlineResult(
  6826. result.bot,
  6827. result.result.get(),
  6828. action,
  6829. result.messageSendingFrom.localId);
  6830. clearFieldText();
  6831. _saveDraftText = true;
  6832. _saveDraftStart = crl::now();
  6833. saveDraft();
  6834. auto &bots = cRefRecentInlineBots();
  6835. const auto index = bots.indexOf(result.bot);
  6836. if (index) {
  6837. if (index > 0) {
  6838. bots.removeAt(index);
  6839. } else if (bots.size() >= RecentInlineBotsLimit) {
  6840. bots.resize(RecentInlineBotsLimit - 1);
  6841. }
  6842. bots.push_front(result.bot);
  6843. session().local().writeRecentHashtagsAndBots();
  6844. }
  6845. hideSelectorControlsAnimated();
  6846. setInnerFocus();
  6847. }
  6848. void HistoryWidget::updatePinnedViewer() {
  6849. if (_firstLoadRequest
  6850. || _delayedShowAtRequest
  6851. || _scroll->isHidden()
  6852. || !_history
  6853. || !_historyInited
  6854. || !_pinnedTracker) {
  6855. return;
  6856. }
  6857. const auto visibleBottom = _scroll->scrollTop() + _scroll->height();
  6858. auto [view, offset] = _list->findViewForPinnedTracking(visibleBottom);
  6859. const auto lessThanId = !view
  6860. ? (ServerMaxMsgId - 1)
  6861. : (view->history() != _history)
  6862. ? (view->data()->id + (offset > 0 ? 1 : 0) - ServerMaxMsgId)
  6863. : (view->data()->id + (offset > 0 ? 1 : 0));
  6864. const auto lastClickedId = !_pinnedClickedId
  6865. ? (ServerMaxMsgId - 1)
  6866. : (!_migrated || peerIsChannel(_pinnedClickedId.peer))
  6867. ? _pinnedClickedId.msg
  6868. : (_pinnedClickedId.msg - ServerMaxMsgId);
  6869. if (_pinnedClickedId
  6870. && lessThanId <= lastClickedId
  6871. && !_scrollToAnimation.animating()) {
  6872. _pinnedClickedId = FullMsgId();
  6873. }
  6874. if (_pinnedClickedId && !_minPinnedId) {
  6875. _minPinnedId = Data::ResolveMinPinnedId(
  6876. _peer,
  6877. MsgId(0), // topicRootId
  6878. _migrated ? _migrated->peer.get() : nullptr);
  6879. }
  6880. if (_pinnedClickedId
  6881. && _minPinnedId
  6882. && (_minPinnedId >= _pinnedClickedId)) {
  6883. // After click on the last pinned message we should the top one.
  6884. _pinnedTracker->trackAround(ServerMaxMsgId - 1);
  6885. } else {
  6886. _pinnedTracker->trackAround(std::min(lessThanId, lastClickedId));
  6887. }
  6888. }
  6889. void HistoryWidget::checkLastPinnedClickedIdReset(
  6890. int wasScrollTop,
  6891. int nowScrollTop) {
  6892. if (_firstLoadRequest
  6893. || _delayedShowAtRequest
  6894. || _scroll->isHidden()
  6895. || !_history
  6896. || !_historyInited) {
  6897. return;
  6898. }
  6899. if (wasScrollTop < nowScrollTop && _pinnedClickedId) {
  6900. // User scrolled down.
  6901. _pinnedClickedId = FullMsgId();
  6902. _minPinnedId = std::nullopt;
  6903. updatePinnedViewer();
  6904. }
  6905. }
  6906. void HistoryWidget::setupTranslateBar() {
  6907. Expects(_history != nullptr);
  6908. _translateBar = std::make_unique<HistoryView::TranslateBar>(
  6909. this,
  6910. controller(),
  6911. _history);
  6912. controller()->adaptive().oneColumnValue(
  6913. ) | rpl::start_with_next([=, raw = _translateBar.get()](bool one) {
  6914. raw->setShadowGeometryPostprocess([=](QRect geometry) {
  6915. if (!one) {
  6916. geometry.setLeft(geometry.left() + st::lineWidth);
  6917. }
  6918. return geometry;
  6919. });
  6920. }, _translateBar->lifetime());
  6921. _translateBarHeight = 0;
  6922. _translateBar->heightValue(
  6923. ) | rpl::start_with_next([=](int height) {
  6924. _topDelta = _preserveScrollTop ? 0 : (height - _translateBarHeight);
  6925. _translateBarHeight = height;
  6926. updateHistoryGeometry();
  6927. updateControlsGeometry();
  6928. _topDelta = 0;
  6929. }, _translateBar->lifetime());
  6930. orderWidgets();
  6931. if (_showAnimation) {
  6932. _translateBar->hide();
  6933. }
  6934. }
  6935. void HistoryWidget::setupPinnedTracker() {
  6936. Expects(_history != nullptr);
  6937. _pinnedTracker = std::make_unique<HistoryView::PinnedTracker>(_history);
  6938. _pinnedBar = nullptr;
  6939. checkPinnedBarState();
  6940. }
  6941. void HistoryWidget::checkPinnedBarState() {
  6942. Expects(_pinnedTracker != nullptr);
  6943. Expects(_list != nullptr);
  6944. const auto hiddenId = _peer->canPinMessages()
  6945. ? MsgId(0)
  6946. : session().settings().hiddenPinnedMessageId(_peer->id);
  6947. const auto currentPinnedId = Data::ResolveTopPinnedId(
  6948. _peer,
  6949. MsgId(0), // topicRootId
  6950. _migrated ? _migrated->peer.get() : nullptr);
  6951. const auto universalPinnedId = !currentPinnedId
  6952. ? int32(0)
  6953. : (_migrated && !peerIsChannel(currentPinnedId.peer))
  6954. ? (currentPinnedId.msg - ServerMaxMsgId)
  6955. : currentPinnedId.msg;
  6956. if (universalPinnedId == hiddenId) {
  6957. if (_pinnedBar) {
  6958. _pinnedBar->setContent(rpl::single(Ui::MessageBarContent()));
  6959. _pinnedTracker->reset();
  6960. _list->setShownPinned(nullptr);
  6961. _hidingPinnedBar = base::take(_pinnedBar);
  6962. const auto raw = _hidingPinnedBar.get();
  6963. base::call_delayed(st::defaultMessageBar.duration, this, [=] {
  6964. if (_hidingPinnedBar.get() == raw) {
  6965. clearHidingPinnedBar();
  6966. }
  6967. });
  6968. }
  6969. return;
  6970. }
  6971. if (_pinnedBar || !universalPinnedId) {
  6972. return;
  6973. }
  6974. clearHidingPinnedBar();
  6975. _pinnedBar = std::make_unique<Ui::PinnedBar>(this, [=] {
  6976. return controller()->isGifPausedAtLeastFor(
  6977. Window::GifPauseReason::Any);
  6978. }, controller()->gifPauseLevelChanged());
  6979. auto pinnedRefreshed = Info::Profile::SharedMediaCountValue(
  6980. _peer,
  6981. MsgId(0), // topicRootId
  6982. nullptr,
  6983. Storage::SharedMediaType::Pinned
  6984. ) | rpl::distinct_until_changed(
  6985. ) | rpl::map([=](int count) {
  6986. if (_pinnedClickedId) {
  6987. _pinnedClickedId = FullMsgId();
  6988. _minPinnedId = std::nullopt;
  6989. updatePinnedViewer();
  6990. }
  6991. return (count > 1);
  6992. }) | rpl::distinct_until_changed();
  6993. auto markupRefreshed = HistoryView::PinnedBarItemWithReplyMarkup(
  6994. &session(),
  6995. _pinnedTracker->shownMessageId());
  6996. rpl::combine(
  6997. rpl::duplicate(pinnedRefreshed),
  6998. rpl::duplicate(markupRefreshed)
  6999. ) | rpl::start_with_next([=](bool many, HistoryItem *item) {
  7000. refreshPinnedBarButton(many, item);
  7001. }, _pinnedBar->lifetime());
  7002. _pinnedBar->setContent(rpl::combine(
  7003. HistoryView::PinnedBarContent(
  7004. &session(),
  7005. _pinnedTracker->shownMessageId(),
  7006. [bar = _pinnedBar.get()] { bar->customEmojiRepaint(); }),
  7007. std::move(pinnedRefreshed),
  7008. std::move(markupRefreshed)
  7009. ) | rpl::map([=](Ui::MessageBarContent &&content, bool, HistoryItem*) {
  7010. const auto id = (!content.title.isEmpty() || !content.text.empty())
  7011. ? _pinnedTracker->currentMessageId().message
  7012. : FullMsgId();
  7013. if (const auto list = _list.data()) {
  7014. // Sometimes we get here with non-empty content and id of
  7015. // message that is being deleted right now. We get here in
  7016. // the moment when _itemRemoved was already fired (so in
  7017. // the _list the _pinnedItem is already cleared) and the
  7018. // MessageUpdate::Flag::Destroyed being fired right now,
  7019. // so the message is still in Data::Session. So we need to
  7020. // call data().message() async, otherwise we get a nearly-
  7021. // destroyed message from it and save the pointer in _list.
  7022. crl::on_main(list, [=] {
  7023. list->setShownPinned(session().data().message(id));
  7024. });
  7025. }
  7026. return std::move(content);
  7027. }));
  7028. controller()->adaptive().oneColumnValue(
  7029. ) | rpl::start_with_next([=, raw = _pinnedBar.get()](bool one) {
  7030. raw->setShadowGeometryPostprocess([=](QRect geometry) {
  7031. if (!one) {
  7032. geometry.setLeft(geometry.left() + st::lineWidth);
  7033. }
  7034. return geometry;
  7035. });
  7036. }, _pinnedBar->lifetime());
  7037. _pinnedBar->barClicks(
  7038. ) | rpl::start_with_next([=] {
  7039. const auto id = _pinnedTracker->currentMessageId();
  7040. if (const auto item = session().data().message(id.message)) {
  7041. controller()->showPeerHistory(
  7042. item->history()->peer,
  7043. Window::SectionShow::Way::Forward,
  7044. item->id);
  7045. if (const auto group = session().data().groups().find(item)) {
  7046. // Hack for the case when a non-first item of an album
  7047. // is pinned and we still want the 'show last after first'.
  7048. _pinnedClickedId = group->items.front()->fullId();
  7049. } else {
  7050. _pinnedClickedId = id.message;
  7051. }
  7052. _minPinnedId = std::nullopt;
  7053. updatePinnedViewer();
  7054. }
  7055. }, _pinnedBar->lifetime());
  7056. _pinnedBarHeight = 0;
  7057. _pinnedBar->heightValue(
  7058. ) | rpl::start_with_next([=](int height) {
  7059. _topDelta = _preserveScrollTop ? 0 : (height - _pinnedBarHeight);
  7060. _pinnedBarHeight = height;
  7061. updateHistoryGeometry();
  7062. updateControlsGeometry();
  7063. _topDelta = 0;
  7064. }, _pinnedBar->lifetime());
  7065. orderWidgets();
  7066. if (_showAnimation) {
  7067. _pinnedBar->hide();
  7068. }
  7069. }
  7070. void HistoryWidget::clearHidingPinnedBar() {
  7071. if (!_hidingPinnedBar) {
  7072. return;
  7073. }
  7074. if (const auto delta = -_pinnedBarHeight) {
  7075. _pinnedBarHeight = 0;
  7076. setGeometryWithTopMoved(geometry(), delta);
  7077. }
  7078. _hidingPinnedBar = nullptr;
  7079. }
  7080. void HistoryWidget::checkMessagesTTL() {
  7081. if (!_peer || !_peer->messagesTTL()) {
  7082. if (_ttlInfo) {
  7083. _ttlInfo = nullptr;
  7084. updateControlsGeometry();
  7085. updateControlsVisibility();
  7086. }
  7087. } else if (!_ttlInfo || _ttlInfo->peer() != _peer) {
  7088. _ttlInfo = std::make_unique<HistoryView::Controls::TTLButton>(
  7089. this,
  7090. controller()->uiShow(),
  7091. _peer);
  7092. orderWidgets();
  7093. updateControlsGeometry();
  7094. updateControlsVisibility();
  7095. }
  7096. }
  7097. void HistoryWidget::setChooseReportMessagesDetails(
  7098. Data::ReportInput reportInput,
  7099. Fn<void(std::vector<MsgId>)> callback) {
  7100. if (!callback) {
  7101. const auto refresh = _chooseForReport && _chooseForReport->active;
  7102. _chooseForReport = nullptr;
  7103. if (_list) {
  7104. _list->clearChooseReportReason();
  7105. }
  7106. if (refresh) {
  7107. clearSelected();
  7108. updateControlsVisibility();
  7109. updateControlsGeometry();
  7110. updateTopBarChooseForReport();
  7111. }
  7112. } else {
  7113. _chooseForReport = std::make_unique<ChooseMessagesForReport>(
  7114. ChooseMessagesForReport{
  7115. .reportInput = reportInput,
  7116. .callback = std::move(callback) });
  7117. }
  7118. }
  7119. void HistoryWidget::refreshPinnedBarButton(bool many, HistoryItem *item) {
  7120. if (!_pinnedBar) {
  7121. return; // It can be in process of hiding.
  7122. }
  7123. const auto openSection = [=] {
  7124. const auto id = _pinnedTracker
  7125. ? _pinnedTracker->currentMessageId()
  7126. : HistoryView::PinnedId();
  7127. if (!id.message) {
  7128. return;
  7129. }
  7130. controller()->showSection(
  7131. std::make_shared<HistoryView::PinnedMemento>(
  7132. _history,
  7133. ((!_migrated || peerIsChannel(id.message.peer))
  7134. ? id.message.msg
  7135. : (id.message.msg - ServerMaxMsgId))));
  7136. };
  7137. if (const auto replyMarkup = item ? item->inlineReplyMarkup() : nullptr) {
  7138. const auto &rows = replyMarkup->data.rows;
  7139. if ((rows.size() == 1) && (rows.front().size() == 1)) {
  7140. const auto text = rows.front().front().text;
  7141. if (!text.isEmpty()) {
  7142. const auto &st = st::historyPinnedBotButton;
  7143. auto button = object_ptr<Ui::RoundButton>(
  7144. this,
  7145. rpl::never<QString>(),
  7146. st);
  7147. const auto label = Ui::CreateChild<Ui::FlatLabel>(
  7148. button.data(),
  7149. text,
  7150. st::historyPinnedBotLabel);
  7151. if (label->width() > st::historyPinnedBotButtonMaxWidth) {
  7152. label->resizeToWidth(st::historyPinnedBotButtonMaxWidth);
  7153. }
  7154. button->setFullWidth(label->width()
  7155. + st.padding.left()
  7156. + st.padding.right()
  7157. + st.height);
  7158. label->moveToLeft(
  7159. st.padding.left() + st.height / 2,
  7160. (button->height() - label->height()) / 2);
  7161. label->setTextColorOverride(st.textFg->c);
  7162. label->setAttribute(Qt::WA_TransparentForMouseEvents);
  7163. button->setTextTransform(
  7164. Ui::RoundButton::TextTransform::NoTransform);
  7165. button->setFullRadius(true);
  7166. button->setClickedCallback([=] {
  7167. Api::ActivateBotCommand(
  7168. _list->prepareClickHandlerContext(item->fullId()),
  7169. 0,
  7170. 0);
  7171. });
  7172. struct State {
  7173. base::unique_qptr<Ui::PopupMenu> menu;
  7174. };
  7175. const auto state = button->lifetime().make_state<State>();
  7176. _pinnedBar->contextMenuRequested(
  7177. ) | rpl::start_with_next([=, raw = button.data()] {
  7178. state->menu = base::make_unique_q<Ui::PopupMenu>(raw);
  7179. state->menu->addAction(
  7180. tr::lng_settings_events_pinned(tr::now),
  7181. openSection);
  7182. state->menu->popup(QCursor::pos());
  7183. }, button->lifetime());
  7184. _pinnedBar->setRightButton(std::move(button));
  7185. return;
  7186. }
  7187. }
  7188. }
  7189. const auto close = !many;
  7190. auto button = object_ptr<Ui::IconButton>(
  7191. this,
  7192. close ? st::historyReplyCancel : st::historyPinnedShowAll);
  7193. button->clicks(
  7194. ) | rpl::start_with_next([=] {
  7195. if (close) {
  7196. hidePinnedMessage();
  7197. } else {
  7198. openSection();
  7199. }
  7200. }, button->lifetime());
  7201. _pinnedBar->setRightButton(std::move(button));
  7202. }
  7203. void HistoryWidget::setupGroupCallBar() {
  7204. Expects(_history != nullptr);
  7205. const auto peer = _history->peer;
  7206. if (!peer->isChannel() && !peer->isChat()) {
  7207. _groupCallBar = nullptr;
  7208. return;
  7209. }
  7210. _groupCallBar = std::make_unique<Ui::GroupCallBar>(
  7211. this,
  7212. HistoryView::GroupCallBarContentByPeer(
  7213. peer,
  7214. st::historyGroupCallUserpics.size,
  7215. false),
  7216. Core::App().appDeactivatedValue());
  7217. controller()->adaptive().oneColumnValue(
  7218. ) | rpl::start_with_next([=](bool one) {
  7219. _groupCallBar->setShadowGeometryPostprocess([=](QRect geometry) {
  7220. if (!one) {
  7221. geometry.setLeft(geometry.left() + st::lineWidth);
  7222. }
  7223. return geometry;
  7224. });
  7225. }, _groupCallBar->lifetime());
  7226. rpl::merge(
  7227. _groupCallBar->barClicks(),
  7228. _groupCallBar->joinClicks()
  7229. ) | rpl::start_with_next([=] {
  7230. const auto peer = _history->peer;
  7231. if (peer->groupCall()) {
  7232. controller()->startOrJoinGroupCall(peer, {});
  7233. }
  7234. }, _groupCallBar->lifetime());
  7235. _groupCallBarHeight = 0;
  7236. _groupCallBar->heightValue(
  7237. ) | rpl::start_with_next([=](int height) {
  7238. _topDelta = _preserveScrollTop ? 0 : (height - _groupCallBarHeight);
  7239. _groupCallBarHeight = height;
  7240. updateHistoryGeometry();
  7241. updateControlsGeometry();
  7242. _topDelta = 0;
  7243. }, _groupCallBar->lifetime());
  7244. orderWidgets();
  7245. if (_showAnimation) {
  7246. _groupCallBar->hide();
  7247. }
  7248. }
  7249. void HistoryWidget::setupRequestsBar() {
  7250. Expects(_history != nullptr);
  7251. const auto peer = _history->peer;
  7252. if (!peer->isChannel() && !peer->isChat()) {
  7253. _requestsBar = nullptr;
  7254. return;
  7255. }
  7256. _requestsBar = std::make_unique<Ui::RequestsBar>(
  7257. this,
  7258. HistoryView::RequestsBarContentByPeer(
  7259. peer,
  7260. st::historyRequestsUserpics.size,
  7261. false));
  7262. controller()->adaptive().oneColumnValue(
  7263. ) | rpl::start_with_next([=](bool one) {
  7264. _requestsBar->setShadowGeometryPostprocess([=](QRect geometry) {
  7265. if (!one) {
  7266. geometry.setLeft(geometry.left() + st::lineWidth);
  7267. }
  7268. return geometry;
  7269. });
  7270. }, _requestsBar->lifetime());
  7271. _requestsBar->barClicks(
  7272. ) | rpl::start_with_next([=] {
  7273. RequestsBoxController::Start(controller(), _peer);
  7274. }, _requestsBar->lifetime());
  7275. _requestsBarHeight = 0;
  7276. _requestsBar->heightValue(
  7277. ) | rpl::start_with_next([=](int height) {
  7278. _topDelta = _preserveScrollTop ? 0 : (height - _requestsBarHeight);
  7279. _requestsBarHeight = height;
  7280. updateHistoryGeometry();
  7281. updateControlsGeometry();
  7282. _topDelta = 0;
  7283. }, _requestsBar->lifetime());
  7284. orderWidgets();
  7285. if (_showAnimation) {
  7286. _requestsBar->hide();
  7287. }
  7288. }
  7289. void HistoryWidget::requestMessageData(MsgId msgId) {
  7290. if (!_peer) {
  7291. return;
  7292. }
  7293. const auto peer = _peer;
  7294. const auto callback = crl::guard(this, [=] {
  7295. messageDataReceived(peer, msgId);
  7296. });
  7297. session().api().requestMessageData(_peer, msgId, callback);
  7298. }
  7299. bool HistoryWidget::checkSponsoredMessageBarVisibility() const {
  7300. const auto h = _list->height()
  7301. - (_kbScroll->isHidden() ? 0 : _kbScroll->height());
  7302. return (h > _scroll->height());
  7303. }
  7304. void HistoryWidget::requestSponsoredMessageBar() {
  7305. if (!_history || !session().sponsoredMessages().isTopBarFor(_history)) {
  7306. return;
  7307. }
  7308. const auto checkState = [=, this] {
  7309. using State = Data::SponsoredMessages::State;
  7310. const auto state = session().sponsoredMessages().state(
  7311. _history);
  7312. _sponsoredMessagesStateKnown = (state != State::None);
  7313. if (state == State::AppendToTopBar) {
  7314. createSponsoredMessageBar();
  7315. if (checkSponsoredMessageBarVisibility()) {
  7316. _sponsoredMessageBar->toggle(true, anim::type::normal);
  7317. } else {
  7318. auto &lifetime = _sponsoredMessageBar->lifetime();
  7319. const auto heightLifetime
  7320. = lifetime.make_state<rpl::lifetime>();
  7321. _list->heightValue(
  7322. ) | rpl::start_with_next([=, this] {
  7323. if (_sponsoredMessageBar->toggled()) {
  7324. heightLifetime->destroy();
  7325. } else if (checkSponsoredMessageBarVisibility()) {
  7326. _sponsoredMessageBar->toggle(
  7327. true,
  7328. anim::type::normal);
  7329. heightLifetime->destroy();
  7330. }
  7331. }, *heightLifetime);
  7332. }
  7333. }
  7334. };
  7335. const auto history = _history;
  7336. session().sponsoredMessages().request(
  7337. _history,
  7338. crl::guard(this, [=, this] {
  7339. if (history == _history) {
  7340. checkState();
  7341. }
  7342. }));
  7343. }
  7344. void HistoryWidget::checkSponsoredMessageBar() {
  7345. if (!_history || !session().sponsoredMessages().isTopBarFor(_history)) {
  7346. return;
  7347. }
  7348. const auto state = session().sponsoredMessages().state(_history);
  7349. if (state == Data::SponsoredMessages::State::AppendToTopBar) {
  7350. if (checkSponsoredMessageBarVisibility()) {
  7351. if (!_sponsoredMessageBar) {
  7352. createSponsoredMessageBar();
  7353. }
  7354. _sponsoredMessageBar->toggle(true, anim::type::instant);
  7355. }
  7356. }
  7357. }
  7358. void HistoryWidget::createSponsoredMessageBar() {
  7359. _sponsoredMessageBar = base::make_unique_q<Ui::SlideWrap<>>(
  7360. this,
  7361. object_ptr<Ui::RpWidget>(this));
  7362. _sponsoredMessageBar->entity()->resizeToWidth(_scroll->width());
  7363. const auto maybeFullId = session().sponsoredMessages().fillTopBar(
  7364. _history,
  7365. _sponsoredMessageBar->entity());
  7366. session().sponsoredMessages().itemRemoved(
  7367. maybeFullId
  7368. ) | rpl::start_with_next([this] {
  7369. _sponsoredMessageBar->toggle(false, anim::type::normal);
  7370. _sponsoredMessageBar->shownValue() | rpl::filter(
  7371. !rpl::mappers::_1
  7372. ) | rpl::start_with_next([this] {
  7373. _sponsoredMessageBar = nullptr;
  7374. }, _sponsoredMessageBar->lifetime());
  7375. }, _sponsoredMessageBar->lifetime());
  7376. if (maybeFullId) {
  7377. const auto viewLifetime
  7378. = _sponsoredMessageBar->lifetime().make_state<rpl::lifetime>();
  7379. rpl::combine(
  7380. _sponsoredMessageBar->entity()->heightValue(),
  7381. _sponsoredMessageBar->heightValue()
  7382. ) | rpl::filter(
  7383. rpl::mappers::_1 == rpl::mappers::_2
  7384. ) | rpl::start_with_next([=] {
  7385. session().sponsoredMessages().view(maybeFullId);
  7386. viewLifetime->destroy();
  7387. }, *viewLifetime);
  7388. }
  7389. _sponsoredMessageBarHeight = 0;
  7390. _sponsoredMessageBar->heightValue(
  7391. ) | rpl::start_with_next([=](int height) {
  7392. _topDelta = _preserveScrollTop
  7393. ? 0
  7394. : (height - _sponsoredMessageBarHeight);
  7395. _sponsoredMessageBarHeight = height;
  7396. updateHistoryGeometry();
  7397. updateControlsGeometry();
  7398. _topDelta = 0;
  7399. }, _sponsoredMessageBar->lifetime());
  7400. _sponsoredMessageBar->toggle(false, anim::type::instant);
  7401. }
  7402. bool HistoryWidget::sendExistingDocument(
  7403. not_null<DocumentData*> document,
  7404. Api::MessageToSend messageToSend,
  7405. std::optional<MsgId> localId) {
  7406. const auto error = _peer
  7407. ? Data::RestrictionError(_peer, ChatRestriction::SendStickers)
  7408. : Data::SendError();
  7409. if (error) {
  7410. Data::ShowSendErrorToast(controller(), _peer, error);
  7411. return false;
  7412. } else if (!_peer
  7413. || !_canSendMessages
  7414. || showSlowmodeError()
  7415. || ShowSendPremiumError(controller(), document)) {
  7416. return false;
  7417. }
  7418. const auto withPaymentApproved = [=](int approved) {
  7419. auto copy = messageToSend;
  7420. copy.action.options.starsApproved = approved;
  7421. sendExistingDocument(document, std::move(copy), localId);
  7422. };
  7423. const auto checked = checkSendPayment(
  7424. 1,
  7425. messageToSend.action.options.starsApproved,
  7426. withPaymentApproved);
  7427. if (!checked) {
  7428. return false;
  7429. }
  7430. Api::SendExistingDocument(
  7431. std::move(messageToSend),
  7432. document,
  7433. localId);
  7434. if (_autocomplete && _autocomplete->stickersShown()) {
  7435. clearFieldText();
  7436. //_saveDraftText = true;
  7437. //_saveDraftStart = crl::now();
  7438. //saveDraft();
  7439. // won't be needed if SendInlineBotResult will clear the cloud draft
  7440. saveCloudDraft();
  7441. }
  7442. hideSelectorControlsAnimated();
  7443. setInnerFocus();
  7444. return true;
  7445. }
  7446. bool HistoryWidget::sendExistingPhoto(
  7447. not_null<PhotoData*> photo,
  7448. Api::SendOptions options) {
  7449. const auto error = _peer
  7450. ? Data::RestrictionError(_peer, ChatRestriction::SendPhotos)
  7451. : Data::SendError();
  7452. if (error) {
  7453. Data::ShowSendErrorToast(controller(), _peer, error);
  7454. return false;
  7455. } else if (!_peer || !_canSendMessages) {
  7456. return false;
  7457. } else if (showSlowmodeError()) {
  7458. return false;
  7459. }
  7460. const auto withPaymentApproved = [=](int approved) {
  7461. auto copy = options;
  7462. copy.starsApproved = approved;
  7463. sendExistingPhoto(photo, copy);
  7464. };
  7465. const auto checked = checkSendPayment(
  7466. 1,
  7467. options.starsApproved,
  7468. withPaymentApproved);
  7469. if (!checked) {
  7470. return false;
  7471. }
  7472. Api::SendExistingPhoto(
  7473. Api::MessageToSend(prepareSendAction(options)),
  7474. photo);
  7475. hideSelectorControlsAnimated();
  7476. setInnerFocus();
  7477. return true;
  7478. }
  7479. void HistoryWidget::showInfoTooltip(
  7480. const TextWithEntities &text,
  7481. Fn<void()> hiddenCallback) {
  7482. _topToast.show(
  7483. _scroll.data(),
  7484. &session(),
  7485. text,
  7486. std::move(hiddenCallback));
  7487. }
  7488. void HistoryWidget::showPremiumStickerTooltip(
  7489. not_null<const HistoryView::Element*> view) {
  7490. if (const auto media = view->data()->media()) {
  7491. if (const auto document = media->document()) {
  7492. showPremiumToast(document);
  7493. }
  7494. }
  7495. }
  7496. void HistoryWidget::showPremiumToast(not_null<DocumentData*> document) {
  7497. if (!_stickerToast) {
  7498. _stickerToast = std::make_unique<HistoryView::StickerToast>(
  7499. controller(),
  7500. this,
  7501. [=] { _stickerToast = nullptr; });
  7502. }
  7503. _stickerToast->showFor(document);
  7504. }
  7505. void HistoryWidget::checkCharsCount() {
  7506. _fieldCharsCountManager.setCount(Ui::ComputeFieldCharacterCount(_field));
  7507. checkCharsLimitation();
  7508. }
  7509. void HistoryWidget::checkCharsLimitation() {
  7510. if (!_history || !_editMsgId) {
  7511. _charsLimitation = nullptr;
  7512. return;
  7513. }
  7514. const auto item = session().data().message(_history->peer, _editMsgId);
  7515. if (!item) {
  7516. _charsLimitation = nullptr;
  7517. return;
  7518. }
  7519. const auto hasMediaWithCaption = item->media()
  7520. && item->media()->allowsEditCaption();
  7521. const auto maxCaptionSize = !hasMediaWithCaption
  7522. ? MaxMessageSize
  7523. : Data::PremiumLimits(&session()).captionLengthCurrent();
  7524. const auto remove = _fieldCharsCountManager.count() - maxCaptionSize;
  7525. if (remove > 0) {
  7526. if (!_charsLimitation) {
  7527. _charsLimitation = base::make_unique_q<CharactersLimitLabel>(
  7528. this,
  7529. _send.get(),
  7530. style::al_bottom);
  7531. _charsLimitation->show();
  7532. Data::AmPremiumValue(
  7533. &session()
  7534. ) | rpl::start_with_next([=] {
  7535. checkCharsLimitation();
  7536. }, _charsLimitation->lifetime());
  7537. }
  7538. _charsLimitation->setLeft(remove);
  7539. } else {
  7540. _charsLimitation = nullptr;
  7541. }
  7542. }
  7543. void HistoryWidget::setFieldText(
  7544. const TextWithTags &textWithTags,
  7545. TextUpdateEvents events,
  7546. FieldHistoryAction fieldHistoryAction) {
  7547. _textUpdateEvents = events;
  7548. _field->setTextWithTags(textWithTags, fieldHistoryAction);
  7549. auto cursor = _field->textCursor();
  7550. cursor.movePosition(QTextCursor::End);
  7551. _field->setTextCursor(cursor);
  7552. _textUpdateEvents = TextUpdateEvent::SaveDraft
  7553. | TextUpdateEvent::SendTyping;
  7554. checkCharsCount();
  7555. if (_preview) {
  7556. _preview->checkNow(false);
  7557. }
  7558. }
  7559. void HistoryWidget::clearFieldText(
  7560. TextUpdateEvents events,
  7561. FieldHistoryAction fieldHistoryAction) {
  7562. setFieldText(TextWithTags(), events, fieldHistoryAction);
  7563. }
  7564. void HistoryWidget::replyToMessage(FullReplyTo id) {
  7565. if (const auto item = session().data().message(id.messageId)) {
  7566. if (CanSendReply(item) && !base::IsCtrlPressed()) {
  7567. replyToMessage(item, id.quote, id.quoteOffset);
  7568. } else if (item->allowsForward()) {
  7569. const auto show = controller()->uiShow();
  7570. HistoryView::Controls::ShowReplyToChatBox(show, id);
  7571. } else {
  7572. controller()->showToast(
  7573. tr::lng_error_cant_reply_other(tr::now));
  7574. }
  7575. }
  7576. }
  7577. void HistoryWidget::replyToMessage(
  7578. not_null<HistoryItem*> item,
  7579. TextWithEntities quote,
  7580. int quoteOffset) {
  7581. if (isJoinChannel()) {
  7582. return;
  7583. }
  7584. _processingReplyTo = {
  7585. .messageId = item->fullId(),
  7586. .quote = quote,
  7587. .quoteOffset = quoteOffset,
  7588. };
  7589. _processingReplyItem = item;
  7590. processReply();
  7591. }
  7592. void HistoryWidget::processReply() {
  7593. const auto processContinue = [=] {
  7594. return crl::guard(_list, [=] {
  7595. if (!_peer || !_processingReplyTo) {
  7596. return;
  7597. } else if (!_processingReplyItem) {
  7598. _processingReplyItem = _peer->owner().message(
  7599. _processingReplyTo.messageId);
  7600. if (!_processingReplyItem) {
  7601. _processingReplyTo = {};
  7602. } else {
  7603. processReply();
  7604. }
  7605. }
  7606. });
  7607. };
  7608. const auto processCancel = [=] {
  7609. _processingReplyTo = {};
  7610. _processingReplyItem = nullptr;
  7611. };
  7612. if (!_peer || !_processingReplyTo) {
  7613. return processCancel();
  7614. } else if (!_processingReplyItem) {
  7615. session().api().requestMessageData(
  7616. session().data().peer(_processingReplyTo.messageId.peer),
  7617. _processingReplyTo.messageId.msg,
  7618. processContinue());
  7619. return;
  7620. #if 0 // Now we can "reply" to old legacy group messages.
  7621. } else if (_processingReplyItem->history() == _migrated) {
  7622. if (_processingReplyItem->isService()) {
  7623. controller()->showToast(tr::lng_reply_cant(tr::now));
  7624. } else {
  7625. const auto itemId = _processingReplyItem->fullId();
  7626. controller()->show(
  7627. Ui::MakeConfirmBox({
  7628. .text = tr::lng_reply_cant_forward(),
  7629. .confirmed = crl::guard(this, [=] {
  7630. controller()->content()->setForwardDraft(
  7631. _history,
  7632. { .ids = { 1, itemId } });
  7633. }),
  7634. .confirmText = tr::lng_selected_forward(),
  7635. }));
  7636. }
  7637. return processCancel();
  7638. #endif
  7639. } else if (!_processingReplyItem->isRegular()) {
  7640. return processCancel();
  7641. } else if (const auto forum = _peer->forum()
  7642. ; forum && _processingReplyItem->history() == _history) {
  7643. const auto topicRootId = _processingReplyItem->topicRootId();
  7644. if (forum->topicDeleted(topicRootId)) {
  7645. return processCancel();
  7646. } else if (const auto topic = forum->topicFor(topicRootId)) {
  7647. if (!Data::CanSendAnything(topic)) {
  7648. return processCancel();
  7649. }
  7650. } else {
  7651. forum->requestTopic(topicRootId, processContinue());
  7652. }
  7653. } else if (!Data::CanSendAnything(_peer)) {
  7654. return processCancel();
  7655. }
  7656. setReplyFieldsFromProcessing();
  7657. }
  7658. void HistoryWidget::setReplyFieldsFromProcessing() {
  7659. if (!_processingReplyTo || !_processingReplyItem) {
  7660. return;
  7661. }
  7662. if (_composeSearch) {
  7663. _composeSearch->hideAnimated();
  7664. }
  7665. const auto id = base::take(_processingReplyTo);
  7666. const auto item = base::take(_processingReplyItem);
  7667. if (_editMsgId) {
  7668. if (const auto localDraft = _history->localDraft({})) {
  7669. localDraft->reply = id;
  7670. } else {
  7671. _history->setLocalDraft(std::make_unique<Data::Draft>(
  7672. TextWithTags(),
  7673. id,
  7674. MessageCursor(),
  7675. Data::WebPageDraft()));
  7676. }
  7677. } else {
  7678. _replyEditMsg = item;
  7679. _replyTo = id;
  7680. updateReplyEditText(_replyEditMsg);
  7681. updateCanSendMessage();
  7682. updateBotKeyboard();
  7683. updateReplyToName();
  7684. updateControlsVisibility();
  7685. updateControlsGeometry();
  7686. updateField();
  7687. refreshTopBarActiveChat();
  7688. }
  7689. _saveDraftText = true;
  7690. _saveDraftStart = crl::now();
  7691. saveDraft();
  7692. setInnerFocus();
  7693. }
  7694. void HistoryWidget::editMessage(
  7695. not_null<HistoryItem*> item,
  7696. const TextSelection &selection) {
  7697. if (_chooseTheme) {
  7698. toggleChooseChatTheme(_peer);
  7699. } else if (_voiceRecordBar->isActive()) {
  7700. controller()->showToast(tr::lng_edit_caption_voice(tr::now));
  7701. return;
  7702. } else if (_composeSearch) {
  7703. _composeSearch->hideAnimated();
  7704. }
  7705. if (isRecording()) {
  7706. // Just fix some strange inconsistency.
  7707. _send->clearState();
  7708. }
  7709. if (!_editMsgId) {
  7710. if (_replyTo || !_field->empty()) {
  7711. _history->setLocalDraft(std::make_unique<Data::Draft>(
  7712. _field,
  7713. _replyTo,
  7714. _preview->draft()));
  7715. } else {
  7716. _history->clearLocalDraft({});
  7717. }
  7718. }
  7719. const auto editData = PrepareEditText(item);
  7720. const auto cursor = MessageCursor {
  7721. int(editData.text.size()),
  7722. int(editData.text.size()),
  7723. Ui::kQFixedMax
  7724. };
  7725. const auto previewDraft = Data::WebPageDraft::FromItem(item);
  7726. _history->setLocalEditDraft(std::make_unique<Data::Draft>(
  7727. editData,
  7728. FullReplyTo{ item->fullId() },
  7729. cursor,
  7730. previewDraft));
  7731. applyDraft();
  7732. updateBotKeyboard();
  7733. if (fieldOrDisabledShown()) {
  7734. _fieldBarCancel->show();
  7735. }
  7736. updateFieldPlaceholder();
  7737. updateMouseTracking();
  7738. updateReplyToName();
  7739. updateControlsGeometry();
  7740. updateField();
  7741. SelectTextInFieldWithMargins(_field, selection);
  7742. _saveDraftText = true;
  7743. _saveDraftStart = crl::now();
  7744. saveDraft();
  7745. setInnerFocus();
  7746. }
  7747. void HistoryWidget::fillSenderUserpicMenu(
  7748. not_null<Ui::PopupMenu*> menu,
  7749. not_null<PeerData*> peer) {
  7750. const auto inGroup = _peer && (_peer->isChat() || _peer->isMegagroup());
  7751. Window::FillSenderUserpicMenu(
  7752. controller(),
  7753. peer,
  7754. (inGroup && _canSendTexts) ? _field.data() : nullptr,
  7755. inGroup ? _peer->owner().history(_peer) : Dialogs::Key(),
  7756. Ui::Menu::CreateAddActionCallback(menu));
  7757. }
  7758. void HistoryWidget::hidePinnedMessage() {
  7759. Expects(_pinnedBar != nullptr);
  7760. const auto id = _pinnedTracker->currentMessageId();
  7761. if (!id.message) {
  7762. return;
  7763. }
  7764. if (_peer->canPinMessages()) {
  7765. Window::ToggleMessagePinned(controller(), id.message, false);
  7766. } else {
  7767. const auto callback = [=] {
  7768. if (_pinnedTracker) {
  7769. checkPinnedBarState();
  7770. }
  7771. };
  7772. Window::HidePinnedBar(
  7773. controller(),
  7774. _peer,
  7775. MsgId(0), // topicRootId
  7776. crl::guard(this, callback));
  7777. }
  7778. }
  7779. bool HistoryWidget::lastForceReplyReplied(const FullMsgId &replyTo) const {
  7780. return _peer
  7781. && (replyTo.peer == _peer->id)
  7782. && _keyboard->forceReply()
  7783. && (_keyboard->forMsgId()
  7784. == FullMsgId(_peer->id, _history->lastKeyboardId))
  7785. && _keyboard->forMsgId().msg == replyTo.msg;
  7786. }
  7787. bool HistoryWidget::lastForceReplyReplied() const {
  7788. return _peer
  7789. && _keyboard->forceReply()
  7790. && _keyboard->forMsgId() == replyTo().messageId
  7791. && (_keyboard->forMsgId()
  7792. == FullMsgId(_peer->id, _history->lastKeyboardId));
  7793. }
  7794. bool HistoryWidget::cancelReply(bool lastKeyboardUsed) {
  7795. bool wasReply = false;
  7796. if (_replyTo) {
  7797. wasReply = true;
  7798. _processingReplyItem = _replyEditMsg = nullptr;
  7799. _processingReplyTo = _replyTo = FullReplyTo();
  7800. mouseMoveEvent(0);
  7801. if (!readyToForward()
  7802. && !_previewDrawPreview
  7803. && !_kbReplyTo) {
  7804. _fieldBarCancel->hide();
  7805. updateMouseTracking();
  7806. }
  7807. updateBotKeyboard();
  7808. refreshTopBarActiveChat();
  7809. updateCanSendMessage();
  7810. updateControlsVisibility();
  7811. updateControlsGeometry();
  7812. update();
  7813. } else if (const auto localDraft
  7814. = (_history ? _history->localDraft({}) : nullptr)) {
  7815. if (localDraft->reply) {
  7816. if (localDraft->textWithTags.text.isEmpty()) {
  7817. _history->clearLocalDraft({});
  7818. } else {
  7819. localDraft->reply = {};
  7820. }
  7821. }
  7822. }
  7823. if (wasReply) {
  7824. _saveDraftText = true;
  7825. _saveDraftStart = crl::now();
  7826. saveDraft();
  7827. }
  7828. if (!_editMsgId
  7829. && _keyboard->singleUse()
  7830. && _keyboard->forceReply()
  7831. && lastKeyboardUsed) {
  7832. if (_kbReplyTo) {
  7833. toggleKeyboard(false);
  7834. }
  7835. }
  7836. return wasReply;
  7837. }
  7838. void HistoryWidget::cancelReplyAfterMediaSend(bool lastKeyboardUsed) {
  7839. if (cancelReply(lastKeyboardUsed)) {
  7840. saveCloudDraft();
  7841. }
  7842. }
  7843. int HistoryWidget::countMembersDropdownHeightMax() const {
  7844. auto result = height()
  7845. - rect::m::sum::v(st::membersInnerDropdown.padding);
  7846. result -= _tabbedSelectorToggle->height();
  7847. accumulate_min(result, st::membersInnerHeightMax);
  7848. return result;
  7849. }
  7850. void HistoryWidget::cancelEdit() {
  7851. if (!_editMsgId) {
  7852. return;
  7853. }
  7854. _canReplaceMedia = _canAddMedia = false;
  7855. _photoEditMedia = nullptr;
  7856. updateReplaceMediaButton();
  7857. _replyEditMsg = nullptr;
  7858. setEditMsgId(0);
  7859. _history->clearLocalEditDraft({});
  7860. applyDraft();
  7861. if (_saveEditMsgRequestId) {
  7862. _history->session().api().request(_saveEditMsgRequestId).cancel();
  7863. _saveEditMsgRequestId = 0;
  7864. }
  7865. _saveDraftText = true;
  7866. _saveDraftStart = crl::now();
  7867. saveDraft();
  7868. mouseMoveEvent(nullptr);
  7869. if (!readyToForward()
  7870. && !_previewDrawPreview
  7871. && !replyTo()) {
  7872. _fieldBarCancel->hide();
  7873. updateMouseTracking();
  7874. }
  7875. auto old = _textUpdateEvents;
  7876. _textUpdateEvents = 0;
  7877. fieldChanged();
  7878. _textUpdateEvents = old;
  7879. updateControlsVisibility();
  7880. updateBotKeyboard();
  7881. updateFieldPlaceholder();
  7882. updateControlsGeometry();
  7883. update();
  7884. }
  7885. void HistoryWidget::cancelFieldAreaState() {
  7886. controller()->hideLayer();
  7887. if (_previewDrawPreview) {
  7888. _preview->apply({ .removed = true });
  7889. } else if (_editMsgId) {
  7890. cancelEdit();
  7891. } else if (_replyTo) {
  7892. cancelReply();
  7893. } else if (readyToForward()) {
  7894. _history->setForwardDraft(MsgId(), {});
  7895. } else if (_kbReplyTo) {
  7896. toggleKeyboard();
  7897. }
  7898. }
  7899. void HistoryWidget::fullInfoUpdated() {
  7900. auto refresh = false;
  7901. if (_list) {
  7902. if (updateCanSendMessage()) {
  7903. refresh = true;
  7904. }
  7905. if (_autocomplete) {
  7906. _autocomplete->requestRefresh();
  7907. }
  7908. _list->refreshAboutView();
  7909. _list->updateBotInfo();
  7910. handlePeerUpdate();
  7911. checkSuggestToGigagroup();
  7912. const auto hasNonEmpty = _history->findFirstNonEmpty();
  7913. const auto readyForBotStart = hasNonEmpty
  7914. || (_history->loadedAtTop() && _history->loadedAtBottom());
  7915. if (readyForBotStart && clearMaybeSendStart() && hasNonEmpty) {
  7916. sendBotStartCommand();
  7917. }
  7918. refreshGiftToChannelShown();
  7919. }
  7920. if (updateCmdStartShown()) {
  7921. refresh = true;
  7922. } else if (!_scroll->isHidden() && _unblock->isHidden() == isBlocked()) {
  7923. refresh = true;
  7924. }
  7925. if (refresh) {
  7926. updateControlsVisibility();
  7927. updateControlsGeometry();
  7928. }
  7929. }
  7930. void HistoryWidget::handlePeerUpdate() {
  7931. bool resize = false;
  7932. updateSendRestriction();
  7933. updateHistoryGeometry();
  7934. if (_peer->isChat() && _peer->asChat()->noParticipantInfo()) {
  7935. session().api().requestFullPeer(_peer);
  7936. } else if (_peer->isUser()
  7937. && ((_peer->asUser()->blockStatus() == UserData::BlockStatus::Unknown)
  7938. || (_peer->asUser()->callsStatus()
  7939. == UserData::CallsStatus::Unknown))) {
  7940. session().api().requestFullPeer(_peer);
  7941. } else if (auto channel = _peer->asMegagroup()) {
  7942. if (!channel->mgInfo->botStatus) {
  7943. session().api().chatParticipants().requestBots(channel);
  7944. }
  7945. if (!channel->mgInfo->adminsLoaded) {
  7946. session().api().chatParticipants().requestAdmins(channel);
  7947. }
  7948. }
  7949. if (!_showAnimation) {
  7950. const auto blockChanged = (_unblock->isHidden() == isBlocked());
  7951. if (blockChanged
  7952. || (!isBlocked()
  7953. && (_joinChannel->isHidden() == isJoinChannel()))) {
  7954. resize = true;
  7955. }
  7956. if (updateCanSendMessage()) {
  7957. resize = true;
  7958. }
  7959. if (blockChanged) {
  7960. _list->refreshAboutView(true);
  7961. _list->updateBotInfo();
  7962. }
  7963. updateControlsVisibility();
  7964. if (resize) {
  7965. updateControlsGeometry();
  7966. }
  7967. }
  7968. }
  7969. bool HistoryWidget::updateCanSendMessage() {
  7970. if (!_peer) {
  7971. return false;
  7972. }
  7973. const auto topic = resolveReplyToTopic();
  7974. const auto allWithoutPolls = Data::AllSendRestrictions()
  7975. & ~ChatRestriction::SendPolls;
  7976. const auto newCanSendMessages = topic
  7977. ? Data::CanSendAnyOf(topic, allWithoutPolls)
  7978. : Data::CanSendAnyOf(_peer, allWithoutPolls);
  7979. const auto newCanSendTexts = topic
  7980. ? Data::CanSend(topic, ChatRestriction::SendOther)
  7981. : Data::CanSend(_peer, ChatRestriction::SendOther);
  7982. if (_canSendMessages == newCanSendMessages
  7983. && _canSendTexts == newCanSendTexts) {
  7984. return false;
  7985. }
  7986. _canSendMessages = newCanSendMessages;
  7987. _canSendTexts = newCanSendTexts;
  7988. if (!_canSendMessages) {
  7989. cancelReply();
  7990. }
  7991. refreshScheduledToggle();
  7992. refreshSilentToggle();
  7993. return true;
  7994. }
  7995. void HistoryWidget::forwardSelected() {
  7996. if (!_list) {
  7997. return;
  7998. }
  7999. const auto weak = Ui::MakeWeak(this);
  8000. Window::ShowForwardMessagesBox(controller(), getSelectedItems(), [=] {
  8001. if (const auto strong = weak.data()) {
  8002. strong->clearSelected();
  8003. }
  8004. });
  8005. }
  8006. void HistoryWidget::confirmDeleteSelected() {
  8007. if (!_list) return;
  8008. auto ids = _list->getSelectedItems();
  8009. if (ids.empty()) {
  8010. return;
  8011. }
  8012. const auto items = session().data().idsToItems(ids);
  8013. if (CanCreateModerateMessagesBox(items)) {
  8014. controller()->show(Box(
  8015. CreateModerateMessagesBox,
  8016. items,
  8017. crl::guard(this, [=] { clearSelected(); })));
  8018. } else {
  8019. auto box = Box<DeleteMessagesBox>(&session(), std::move(ids));
  8020. box->setDeleteConfirmedCallback(crl::guard(this, [=] {
  8021. clearSelected();
  8022. }));
  8023. controller()->show(std::move(box));
  8024. }
  8025. }
  8026. void HistoryWidget::escape() {
  8027. if (_composeSearch) {
  8028. if (_nonEmptySelection) {
  8029. clearSelected();
  8030. } else {
  8031. _composeSearch->hideAnimated();
  8032. }
  8033. } else if (_chooseForReport) {
  8034. controller()->clearChooseReportMessages();
  8035. } else if (_nonEmptySelection && _list) {
  8036. clearSelected();
  8037. } else if (_isInlineBot) {
  8038. cancelInlineBot();
  8039. } else if (_editMsgId) {
  8040. if (_replyEditMsg
  8041. && EditTextChanged(_replyEditMsg, _field->getTextWithTags())) {
  8042. controller()->show(Ui::MakeConfirmBox({
  8043. .text = tr::lng_cancel_edit_post_sure(),
  8044. .confirmed = crl::guard(this, [this](Fn<void()> &&close) {
  8045. if (_editMsgId) {
  8046. cancelEdit();
  8047. close();
  8048. }
  8049. }),
  8050. .confirmText = tr::lng_cancel_edit_post_yes(),
  8051. .cancelText = tr::lng_cancel_edit_post_no(),
  8052. }));
  8053. } else {
  8054. cancelEdit();
  8055. }
  8056. } else if (_autocomplete && !_autocomplete->isHidden()) {
  8057. _autocomplete->hideAnimated();
  8058. } else if (_replyTo && _field->getTextWithTags().text.isEmpty()) {
  8059. cancelReply();
  8060. } else if (auto &voice = _voiceRecordBar; voice->isActive()) {
  8061. voice->showDiscardBox(nullptr, anim::type::normal);
  8062. } else {
  8063. _cancelRequests.fire({});
  8064. }
  8065. }
  8066. void HistoryWidget::clearSelected() {
  8067. if (_list) {
  8068. _list->clearSelected();
  8069. }
  8070. }
  8071. HistoryItem *HistoryWidget::getItemFromHistoryOrMigrated(
  8072. MsgId genericMsgId) const {
  8073. return (genericMsgId < 0 && -genericMsgId < ServerMaxMsgId && _migrated)
  8074. ? session().data().message(_migrated->peer, -genericMsgId)
  8075. : _peer
  8076. ? session().data().message(_peer, genericMsgId)
  8077. : nullptr;
  8078. }
  8079. MessageIdsList HistoryWidget::getSelectedItems() const {
  8080. return _list ? _list->getSelectedItems() : MessageIdsList();
  8081. }
  8082. void HistoryWidget::updateTopBarChooseForReport() {
  8083. if (_chooseForReport && _chooseForReport->active) {
  8084. _topBar->showChooseMessagesForReport(
  8085. _chooseForReport->reportInput);
  8086. } else {
  8087. _topBar->clearChooseMessagesForReport();
  8088. }
  8089. updateTopBarSelection();
  8090. updateControlsVisibility();
  8091. updateControlsGeometry();
  8092. }
  8093. void HistoryWidget::updateTopBarSelection() {
  8094. if (!_list) {
  8095. _topBar->showSelected(HistoryView::TopBarWidget::SelectedState {});
  8096. return;
  8097. }
  8098. auto selectedState = _list->getSelectionState();
  8099. _nonEmptySelection = (selectedState.count > 0)
  8100. || selectedState.textSelected;
  8101. _topBar->showSelected(selectedState);
  8102. if ((selectedState.count > 0) && _composeSearch) {
  8103. _composeSearch->hideAnimated();
  8104. }
  8105. const auto transparent = Qt::WA_TransparentForMouseEvents;
  8106. if (selectedState.count == 0) {
  8107. _reportMessages->clearState();
  8108. _reportMessages->setAttribute(transparent);
  8109. _reportMessages->setColorOverride(st::windowSubTextFg->c);
  8110. } else if (_reportMessages->testAttribute(transparent)) {
  8111. _reportMessages->setAttribute(transparent, false);
  8112. _reportMessages->setColorOverride(std::nullopt);
  8113. }
  8114. _reportMessages->setText(Ui::Text::Upper(selectedState.count
  8115. ? tr::lng_report_messages_count(
  8116. tr::now,
  8117. lt_count,
  8118. selectedState.count)
  8119. : tr::lng_report_messages_none(tr::now)));
  8120. updateControlsVisibility();
  8121. updateHistoryGeometry();
  8122. if (!controller()->isLayerShown()
  8123. && !Core::App().passcodeLocked()) {
  8124. if (isSearching() && !_nonEmptySelection) {
  8125. _composeSearch->setInnerFocus();
  8126. } else if (_nonEmptySelection
  8127. || (_list && _list->wasSelectedText())
  8128. || isRecording()
  8129. || isBotStart()
  8130. || isBlocked()
  8131. || (!_canSendTexts && !_editMsgId)) {
  8132. _list->setFocus();
  8133. } else {
  8134. _field->setFocus();
  8135. }
  8136. }
  8137. _topBar->update();
  8138. update();
  8139. }
  8140. void HistoryWidget::messageDataReceived(
  8141. not_null<PeerData*> peer,
  8142. MsgId msgId) {
  8143. if (!_peer || _peer != peer || !msgId) {
  8144. return;
  8145. } else if (_editMsgId == msgId
  8146. || (_replyTo.messageId == FullMsgId(peer->id, msgId))) {
  8147. updateReplyEditTexts(true);
  8148. if (_editMsgId == msgId) {
  8149. _preview->setDisabled(_editMsgId
  8150. && _replyEditMsg
  8151. && _replyEditMsg->media()
  8152. && !_replyEditMsg->media()->webpage());
  8153. }
  8154. }
  8155. }
  8156. void HistoryWidget::updateReplyEditText(not_null<HistoryItem*> item) {
  8157. const auto context = Core::TextContext({
  8158. .session = &session(),
  8159. .repaint = [=] { updateField(); },
  8160. });
  8161. _replyEditMsgText.setMarkedText(
  8162. st::defaultTextStyle,
  8163. ((_editMsgId || _replyTo.quote.empty())
  8164. ? item->inReplyText()
  8165. : _replyTo.quote),
  8166. Ui::DialogTextOptions(),
  8167. context);
  8168. if (fieldOrDisabledShown() || isRecording()) {
  8169. _fieldBarCancel->show();
  8170. updateMouseTracking();
  8171. }
  8172. }
  8173. void HistoryWidget::updateReplyEditTexts(bool force) {
  8174. if (!force) {
  8175. if (_replyEditMsg || (!_editMsgId && !_replyTo)) {
  8176. return;
  8177. }
  8178. }
  8179. if (!_replyEditMsg && _peer) {
  8180. _replyEditMsg = session().data().message(
  8181. _editMsgId ? _peer->id : _replyTo.messageId.peer,
  8182. _editMsgId ? _editMsgId : _replyTo.messageId.msg);
  8183. if (!_editMsgId) {
  8184. updateFieldPlaceholder();
  8185. }
  8186. }
  8187. if (_replyEditMsg) {
  8188. const auto editMedia = _editMsgId
  8189. ? _replyEditMsg->media()
  8190. : nullptr;
  8191. if (_editMsgId && _replyEditMsg) {
  8192. _mediaEditManager.start(_replyEditMsg);
  8193. }
  8194. _canReplaceMedia = _editMsgId && _replyEditMsg->allowsEditMedia();
  8195. if (editMedia && editMedia->allowsEditMedia()) {
  8196. _canAddMedia = false;
  8197. } else {
  8198. _canAddMedia = base::take(_canReplaceMedia);
  8199. }
  8200. if (_canReplaceMedia || _canAddMedia) {
  8201. // Invalidate the button, maybe icon has changed.
  8202. _replaceMedia.destroy();
  8203. }
  8204. _photoEditMedia = (_canReplaceMedia
  8205. && editMedia->photo()
  8206. && !editMedia->photo()->isNull())
  8207. ? editMedia->photo()->createMediaView()
  8208. : nullptr;
  8209. if (_photoEditMedia) {
  8210. _photoEditMedia->wanted(
  8211. Data::PhotoSize::Large,
  8212. _replyEditMsg->fullId());
  8213. }
  8214. if (updateReplaceMediaButton()) {
  8215. updateControlsVisibility();
  8216. updateControlsGeometry();
  8217. }
  8218. updateReplyEditText(_replyEditMsg);
  8219. updateBotKeyboard();
  8220. updateReplyToName();
  8221. updateField();
  8222. } else if (force) {
  8223. if (_editMsgId) {
  8224. cancelEdit();
  8225. } else {
  8226. cancelReply();
  8227. }
  8228. }
  8229. }
  8230. void HistoryWidget::updateForwarding() {
  8231. _forwardPanel->update(_history, _history
  8232. ? _history->resolveForwardDraft(MsgId())
  8233. : Data::ResolvedForwardDraft());
  8234. updateControlsVisibility();
  8235. updateControlsGeometry();
  8236. }
  8237. void HistoryWidget::updateReplyToName() {
  8238. if (!_history || _editMsgId) {
  8239. return;
  8240. } else if (!_replyEditMsg && (_replyTo || !_kbReplyTo)) {
  8241. return;
  8242. }
  8243. const auto context = Core::TextContext({
  8244. .session = &_history->session(),
  8245. .customEmojiLoopLimit = 1,
  8246. });
  8247. const auto to = _replyEditMsg ? _replyEditMsg : _kbReplyTo;
  8248. const auto replyToQuote = _replyTo && !_replyTo.quote.empty();
  8249. _replyToName.setMarkedText(
  8250. st::fwdTextStyle,
  8251. HistoryView::Reply::ComposePreviewName(_history, to, replyToQuote),
  8252. Ui::NameTextOptions(),
  8253. context);
  8254. }
  8255. void HistoryWidget::updateField() {
  8256. if (_repaintFieldScheduled) {
  8257. return;
  8258. }
  8259. _repaintFieldScheduled = true;
  8260. const auto fieldAreaTop = _scroll->y() + _scroll->height();
  8261. rtlupdate(0, fieldAreaTop, width(), height() - fieldAreaTop);
  8262. }
  8263. void HistoryWidget::drawField(Painter &p, const QRect &rect) {
  8264. _repaintFieldScheduled = false;
  8265. auto backy = _field->y() - st::historySendPadding;
  8266. auto backh = fieldHeight() + 2 * st::historySendPadding;
  8267. auto hasForward = readyToForward();
  8268. auto drawMsgText = (_editMsgId || _replyTo) ? _replyEditMsg : _kbReplyTo;
  8269. if (_editMsgId || _replyTo || (!hasForward && _kbReplyTo)) {
  8270. backy -= st::historyReplyHeight;
  8271. backh += st::historyReplyHeight;
  8272. } else if (hasForward) {
  8273. backy -= st::historyReplyHeight;
  8274. backh += st::historyReplyHeight;
  8275. } else if (_previewDrawPreview) {
  8276. backy -= st::historyReplyHeight;
  8277. backh += st::historyReplyHeight;
  8278. }
  8279. p.setInactive(
  8280. controller()->isGifPausedAtLeastFor(Window::GifPauseReason::Any));
  8281. p.fillRect(myrtlrect(0, backy, width(), backh), st::historyReplyBg);
  8282. const auto media = (!_previewDrawPreview && drawMsgText)
  8283. ? drawMsgText->media()
  8284. : nullptr;
  8285. const auto hasPreview = media && media->hasReplyPreview();
  8286. const auto preview = _mediaEditManager
  8287. ? _mediaEditManager.mediaPreview()
  8288. : hasPreview
  8289. ? media->replyPreview()
  8290. : nullptr;
  8291. const auto spoilered = _mediaEditManager.spoilered();
  8292. if (!spoilered) {
  8293. _replySpoiler = nullptr;
  8294. } else if (!_replySpoiler) {
  8295. _replySpoiler = std::make_unique<Ui::SpoilerAnimation>([=] {
  8296. updateField();
  8297. });
  8298. }
  8299. if (_previewDrawPreview) {
  8300. st::historyLinkIcon.paint(
  8301. p,
  8302. st::historyReplyIconPosition + QPoint(0, backy),
  8303. width());
  8304. const auto textTop = backy + st::msgReplyPadding.top();
  8305. auto previewLeft = st::historyReplySkip;
  8306. const auto to = QRect(
  8307. previewLeft,
  8308. backy + (st::historyReplyHeight - st::historyReplyPreview) / 2,
  8309. st::historyReplyPreview,
  8310. st::historyReplyPreview);
  8311. if (_previewDrawPreview(p, to)) {
  8312. previewLeft += st::historyReplyPreview + st::msgReplyBarSkip;
  8313. }
  8314. p.setPen(st::historyReplyNameFg);
  8315. const auto elidedWidth = width()
  8316. - previewLeft
  8317. - _fieldBarCancel->width()
  8318. - st::msgReplyPadding.right();
  8319. _previewTitle.drawElided(
  8320. p,
  8321. previewLeft,
  8322. textTop,
  8323. elidedWidth);
  8324. p.setPen(st::historyComposeAreaFg);
  8325. _previewDescription.drawElided(
  8326. p,
  8327. previewLeft,
  8328. textTop + st::msgServiceNameFont->height,
  8329. elidedWidth);
  8330. } else if (_editMsgId || _replyTo || (!hasForward && _kbReplyTo)) {
  8331. const auto now = crl::now();
  8332. const auto paused = p.inactive();
  8333. const auto pausedSpoiler = paused || On(PowerSaving::kChatSpoiler);
  8334. auto replyLeft = st::historyReplySkip;
  8335. (_editMsgId
  8336. ? st::historyEditIcon
  8337. : (_replyTo && !_replyTo.quote.empty())
  8338. ? st::historyQuoteIcon
  8339. : st::historyReplyIcon).paint(
  8340. p,
  8341. st::historyReplyIconPosition + QPoint(0, backy),
  8342. width());
  8343. if (drawMsgText) {
  8344. if (hasPreview) {
  8345. if (preview) {
  8346. const auto overEdit = _photoEditMedia
  8347. ? _inPhotoEditOver.value(_inPhotoEdit ? 1. : 0.)
  8348. : 0.;
  8349. auto to = QRect(
  8350. replyLeft,
  8351. (st::historyReplyHeight - st::historyReplyPreview) / 2
  8352. + backy,
  8353. st::historyReplyPreview,
  8354. st::historyReplyPreview);
  8355. p.drawPixmap(to.x(), to.y(), preview->pixSingle(
  8356. preview->size() / style::DevicePixelRatio(),
  8357. {
  8358. .options = Images::Option::RoundSmall,
  8359. .outer = to.size(),
  8360. }));
  8361. if (_replySpoiler) {
  8362. if (overEdit > 0.) {
  8363. p.setOpacity(1. - overEdit);
  8364. }
  8365. Ui::FillSpoilerRect(
  8366. p,
  8367. to,
  8368. Ui::DefaultImageSpoiler().frame(
  8369. _replySpoiler->index(now, pausedSpoiler)));
  8370. }
  8371. if (overEdit > 0.) {
  8372. p.setOpacity(overEdit);
  8373. p.fillRect(to, st::historyEditMediaBg);
  8374. st::historyEditMedia.paintInCenter(p, to);
  8375. p.setOpacity(1.);
  8376. }
  8377. }
  8378. replyLeft += st::historyReplyPreview + st::msgReplyBarSkip;
  8379. }
  8380. p.setPen(st::historyReplyNameFg);
  8381. if (_editMsgId) {
  8382. paintEditHeader(p, rect, replyLeft, backy);
  8383. } else {
  8384. _replyToName.drawElided(
  8385. p,
  8386. replyLeft,
  8387. backy + st::msgReplyPadding.top(),
  8388. width()
  8389. - replyLeft
  8390. - _fieldBarCancel->width()
  8391. - st::msgReplyPadding.right());
  8392. }
  8393. p.setPen(st::historyComposeAreaFg);
  8394. _replyEditMsgText.draw(p, {
  8395. .position = QPoint(
  8396. replyLeft,
  8397. st::msgReplyPadding.top()
  8398. + st::msgServiceNameFont->height
  8399. + backy),
  8400. .availableWidth = width()
  8401. - replyLeft
  8402. - _fieldBarCancel->width()
  8403. - st::msgReplyPadding.right(),
  8404. .palette = &st::historyComposeAreaPalette,
  8405. .spoiler = Ui::Text::DefaultSpoilerCache(),
  8406. .now = now,
  8407. .pausedEmoji = paused || On(PowerSaving::kEmojiChat),
  8408. .pausedSpoiler = pausedSpoiler,
  8409. .elisionLines = 1,
  8410. });
  8411. } else {
  8412. p.setFont(st::msgDateFont);
  8413. p.setPen(st::historyComposeAreaFgService);
  8414. p.drawText(
  8415. replyLeft,
  8416. backy
  8417. + (st::historyReplyHeight - st::msgDateFont->height) / 2
  8418. + st::msgDateFont->ascent,
  8419. st::msgDateFont->elided(
  8420. tr::lng_profile_loading(tr::now),
  8421. width()
  8422. - replyLeft
  8423. - _fieldBarCancel->width()
  8424. - st::msgReplyPadding.right()));
  8425. }
  8426. } else if (hasForward) {
  8427. st::historyForwardIcon.paint(
  8428. p,
  8429. st::historyReplyIconPosition + QPoint(0, backy), width());
  8430. const auto x = st::historyReplySkip;
  8431. const auto available = width()
  8432. - x
  8433. - _fieldBarCancel->width()
  8434. - st::msgReplyPadding.right();
  8435. _forwardPanel->paint(p, x, backy, available, width());
  8436. }
  8437. }
  8438. void HistoryWidget::paintEditHeader(
  8439. Painter &p,
  8440. const QRect &rect,
  8441. int left,
  8442. int top) const {
  8443. if (!rect.intersects(
  8444. myrtlrect(left, top, width() - left, st::normalFont->height))) {
  8445. return;
  8446. }
  8447. p.setFont(st::msgServiceNameFont);
  8448. p.drawTextLeft(
  8449. left,
  8450. top + st::msgReplyPadding.top(),
  8451. width(),
  8452. tr::lng_edit_message(tr::now));
  8453. if (!_replyEditMsg
  8454. || _replyEditMsg->history()->peer->canEditMessagesIndefinitely()) {
  8455. return;
  8456. }
  8457. auto editTimeLeftText = QString();
  8458. auto updateIn = int(-1);
  8459. auto timeSinceMessage = ItemDateTime(_replyEditMsg).msecsTo(
  8460. QDateTime::currentDateTime());
  8461. auto editTimeLeft = (session().serverConfig().editTimeLimit * 1000LL)
  8462. - timeSinceMessage;
  8463. if (editTimeLeft < 2) {
  8464. editTimeLeftText = u"0:00"_q;
  8465. } else if (editTimeLeft > kDisplayEditTimeWarningMs) {
  8466. updateIn = static_cast<int>(qMin(
  8467. editTimeLeft - kDisplayEditTimeWarningMs,
  8468. qint64(kFullDayInMs)));
  8469. } else {
  8470. updateIn = static_cast<int>(editTimeLeft % 1000);
  8471. if (!updateIn) {
  8472. updateIn = 1000;
  8473. }
  8474. ++updateIn;
  8475. editTimeLeft = (editTimeLeft - 1) / 1000; // seconds
  8476. editTimeLeftText = u"%1:%2"_q
  8477. .arg(editTimeLeft / 60)
  8478. .arg(editTimeLeft % 60, 2, 10, QChar('0'));
  8479. }
  8480. // Restart timer only if we are sure that we've painted the whole timer.
  8481. if (rect.contains(
  8482. myrtlrect(left, top, width() - left, st::normalFont->height))
  8483. && (updateIn > 0)) {
  8484. _updateEditTimeLeftDisplay.callOnce(updateIn);
  8485. }
  8486. if (!editTimeLeftText.isEmpty()) {
  8487. p.setFont(st::normalFont);
  8488. p.setPen(st::historyComposeAreaFgService);
  8489. p.drawText(
  8490. left
  8491. + st::msgServiceNameFont->width(tr::lng_edit_message(tr::now))
  8492. + st::normalFont->spacew,
  8493. top + st::msgReplyPadding.top() + st::msgServiceNameFont->ascent,
  8494. editTimeLeftText);
  8495. }
  8496. }
  8497. bool HistoryWidget::paintShowAnimationFrame() {
  8498. if (_showAnimation) {
  8499. auto p = QPainter(this);
  8500. _showAnimation->paintContents(p);
  8501. return true;
  8502. }
  8503. return false;
  8504. }
  8505. void HistoryWidget::paintEvent(QPaintEvent *e) {
  8506. if (paintShowAnimationFrame()
  8507. || controller()->contentOverlapped(this, e)) {
  8508. return;
  8509. }
  8510. if (hasPendingResizedItems()) {
  8511. updateListSize();
  8512. }
  8513. Window::SectionWidget::PaintBackground(
  8514. controller(),
  8515. controller()->currentChatTheme(),
  8516. this,
  8517. e->rect());
  8518. Painter p(this);
  8519. const auto clip = e->rect();
  8520. if (_list) {
  8521. const auto restrictionHidden = fieldOrDisabledShown()
  8522. || isRecording();
  8523. if (restrictionHidden
  8524. || replyTo()
  8525. || readyToForward()
  8526. || _kbShown) {
  8527. if (!isSearching()) {
  8528. drawField(p, clip);
  8529. }
  8530. }
  8531. } else {
  8532. const auto w = 0
  8533. + st::msgServiceFont->width(tr::lng_willbe_history(tr::now))
  8534. + st::msgPadding.left()
  8535. + st::msgPadding.right();
  8536. const auto h = st::msgServiceFont->height
  8537. + st::msgServicePadding.top()
  8538. + st::msgServicePadding.bottom();
  8539. const auto tr = QRect(
  8540. (width() - w) / 2,
  8541. st::msgServiceMargin.top() + (height()
  8542. - fieldHeight()
  8543. - 2 * st::historySendPadding
  8544. - h
  8545. - st::msgServiceMargin.top()
  8546. - st::msgServiceMargin.bottom()) / 2,
  8547. w,
  8548. h);
  8549. const auto st = controller()->chatStyle();
  8550. HistoryView::ServiceMessagePainter::PaintBubble(p, st, tr);
  8551. p.setPen(st->msgServiceFg());
  8552. p.setFont(st::msgServiceFont->f);
  8553. p.drawTextLeft(
  8554. tr.left() + st::msgPadding.left(),
  8555. tr.top() + st::msgServicePadding.top(),
  8556. width(),
  8557. tr::lng_willbe_history(tr::now));
  8558. }
  8559. }
  8560. QPoint HistoryWidget::clampMousePosition(QPoint point) {
  8561. if (point.x() < 0) {
  8562. point.setX(0);
  8563. } else if (point.x() >= _scroll->width()) {
  8564. point.setX(_scroll->width() - 1);
  8565. }
  8566. if (point.y() < _scroll->scrollTop()) {
  8567. point.setY(_scroll->scrollTop());
  8568. } else if (point.y() >= _scroll->scrollTop() + _scroll->height()) {
  8569. point.setY(_scroll->scrollTop() + _scroll->height() - 1);
  8570. }
  8571. return point;
  8572. }
  8573. bool HistoryWidget::touchScroll(const QPoint &delta) {
  8574. const auto scTop = _scroll->scrollTop();
  8575. const auto scMax = _scroll->scrollTopMax();
  8576. const auto scNew = std::clamp(scTop - delta.y(), 0, scMax);
  8577. if (scNew == scTop) {
  8578. return false;
  8579. }
  8580. _scroll->scrollToY(scNew);
  8581. return true;
  8582. }
  8583. void HistoryWidget::synteticScrollToY(int y) {
  8584. _synteticScrollEvent = true;
  8585. if (_scroll->scrollTop() == y) {
  8586. visibleAreaUpdated();
  8587. } else {
  8588. _scroll->scrollToY(y);
  8589. }
  8590. _synteticScrollEvent = false;
  8591. }
  8592. HistoryWidget::~HistoryWidget() {
  8593. if (_history) {
  8594. // Saving a draft on account switching.
  8595. saveFieldToHistoryLocalDraft();
  8596. session().api().saveDraftToCloudDelayed(_history);
  8597. setHistory(nullptr);
  8598. session().data().itemVisibilitiesUpdated();
  8599. }
  8600. setTabbedPanel(nullptr);
  8601. }