passport_panel_edit_document.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
  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 "passport/passport_panel_edit_document.h"
  8. #include "passport/passport_panel_controller.h"
  9. #include "passport/passport_panel_edit_scans.h"
  10. #include "passport/ui/passport_details_row.h"
  11. #include "ui/effects/scroll_content_shadow.h"
  12. #include "ui/widgets/fields/input_field.h"
  13. #include "ui/widgets/scroll_area.h"
  14. #include "ui/widgets/labels.h"
  15. #include "ui/widgets/buttons.h"
  16. #include "ui/widgets/checkbox.h"
  17. #include "ui/wrap/vertical_layout.h"
  18. #include "ui/wrap/fade_wrap.h"
  19. #include "ui/wrap/slide_wrap.h"
  20. #include "countries/countries_instance.h"
  21. #include "data/data_user.h" // ->bot()->session()
  22. #include "main/main_session.h" // ->session().user()
  23. #include "ui/text/text_utilities.h" // Ui::Text::ToUpper
  24. #include "boxes/abstract_box.h"
  25. #include "ui/boxes/confirm_box.h"
  26. #include "lang/lang_keys.h"
  27. #include "styles/style_widgets.h"
  28. #include "styles/style_layers.h"
  29. #include "styles/style_passport.h"
  30. namespace Passport {
  31. namespace {
  32. class RequestTypeBox : public Ui::BoxContent {
  33. public:
  34. RequestTypeBox(
  35. QWidget*,
  36. rpl::producer<QString> title,
  37. const QString &about,
  38. std::vector<QString> labels,
  39. Fn<void(int index)> submit);
  40. protected:
  41. void prepare() override;
  42. private:
  43. void setupControls(
  44. const QString &about,
  45. std::vector<QString> labels,
  46. Fn<void(int index)> submit);
  47. rpl::producer<QString> _title;
  48. Fn<void()> _submit;
  49. int _height = 0;
  50. };
  51. class DeleteDocumentBox : public Ui::BoxContent {
  52. public:
  53. DeleteDocumentBox(
  54. QWidget*,
  55. const QString &text,
  56. const QString &detailsCheckbox,
  57. Fn<void(bool withDetails)> submit);
  58. protected:
  59. void prepare() override;
  60. private:
  61. void setupControls(
  62. const QString &text,
  63. const QString &detailsCheckbox,
  64. Fn<void(bool withDetails)> submit);
  65. Fn<void()> _submit;
  66. int _height = 0;
  67. };
  68. RequestTypeBox::RequestTypeBox(
  69. QWidget*,
  70. rpl::producer<QString> title,
  71. const QString &about,
  72. std::vector<QString> labels,
  73. Fn<void(int index)> submit)
  74. : _title(std::move(title)) {
  75. setupControls(about, std::move(labels), submit);
  76. }
  77. void RequestTypeBox::prepare() {
  78. setTitle(std::move(_title));
  79. addButton(tr::lng_passport_upload_document(), _submit);
  80. addButton(tr::lng_cancel(), [=] { closeBox(); });
  81. setDimensions(st::boxWidth, _height);
  82. }
  83. void RequestTypeBox::setupControls(
  84. const QString &about,
  85. std::vector<QString> labels,
  86. Fn<void(int index)> submit) {
  87. const auto header = Ui::CreateChild<Ui::FlatLabel>(
  88. this,
  89. tr::lng_passport_document_type(tr::now),
  90. st::boxDividerLabel);
  91. const auto group = std::make_shared<Ui::RadiobuttonGroup>(0);
  92. auto buttons = std::vector<QPointer<Ui::Radiobutton>>();
  93. auto index = 0;
  94. for (const auto &label : labels) {
  95. buttons.emplace_back(Ui::CreateChild<Ui::Radiobutton>(
  96. this,
  97. group,
  98. index++,
  99. label,
  100. st::defaultBoxCheckbox));
  101. }
  102. const auto description = Ui::CreateChild<Ui::FlatLabel>(
  103. this,
  104. about,
  105. st::boxDividerLabel);
  106. auto y = 0;
  107. const auto innerWidth = st::boxWidth
  108. - st::boxPadding.left()
  109. - st::boxPadding.right();
  110. header->resizeToWidth(innerWidth);
  111. header->moveToLeft(st::boxPadding.left(), y);
  112. y += header->height() + st::passportRequestTypeSkip;
  113. for (const auto &button : buttons) {
  114. button->resizeToNaturalWidth(innerWidth);
  115. button->moveToLeft(st::boxPadding.left(), y);
  116. y += button->heightNoMargins() + st::passportRequestTypeSkip;
  117. }
  118. description->resizeToWidth(innerWidth);
  119. description->moveToLeft(st::boxPadding.left(), y);
  120. y += description->height() + st::passportRequestTypeSkip;
  121. _height = y;
  122. _submit = [=] {
  123. const auto value = group->hasValue() ? group->current() : -1;
  124. if (value >= 0) {
  125. submit(value);
  126. }
  127. };
  128. }
  129. DeleteDocumentBox::DeleteDocumentBox(
  130. QWidget*,
  131. const QString &text,
  132. const QString &detailsCheckbox,
  133. Fn<void(bool withDetails)> submit) {
  134. setupControls(text, detailsCheckbox, submit);
  135. }
  136. void DeleteDocumentBox::prepare() {
  137. addButton(tr::lng_box_delete(), _submit);
  138. addButton(tr::lng_cancel(), [=] { closeBox(); });
  139. setDimensions(st::boxWidth, _height);
  140. }
  141. void DeleteDocumentBox::setupControls(
  142. const QString &text,
  143. const QString &detailsCheckbox,
  144. Fn<void(bool withDetails)> submit) {
  145. const auto label = Ui::CreateChild<Ui::FlatLabel>(
  146. this,
  147. text,
  148. st::boxLabel);
  149. const auto details = !detailsCheckbox.isEmpty()
  150. ? Ui::CreateChild<Ui::Checkbox>(
  151. this,
  152. detailsCheckbox,
  153. false,
  154. st::defaultBoxCheckbox)
  155. : nullptr;
  156. _height = st::boxPadding.top();
  157. const auto availableWidth = st::boxWidth
  158. - st::boxPadding.left()
  159. - st::boxPadding.right();
  160. label->resizeToWidth(availableWidth);
  161. label->moveToLeft(st::boxPadding.left(), _height);
  162. _height += label->height();
  163. if (details) {
  164. _height += st::boxPadding.bottom();
  165. details->moveToLeft(st::boxPadding.left(), _height);
  166. _height += details->heightNoMargins();
  167. }
  168. _height += st::boxPadding.bottom();
  169. _submit = [=] {
  170. submit(details ? details->checked() : false);
  171. };
  172. }
  173. } // namespace
  174. struct PanelEditDocument::Result {
  175. ValueMap data;
  176. ValueMap filesData;
  177. };
  178. PanelEditDocument::PanelEditDocument(
  179. QWidget*,
  180. not_null<PanelController*> controller,
  181. Scheme scheme,
  182. const QString &error,
  183. const ValueMap &data,
  184. const QString &scansError,
  185. const ValueMap &scansData,
  186. ScanListData &&scans,
  187. std::optional<ScanListData> &&translations,
  188. std::map<FileType, ScanInfo> &&specialFiles)
  189. : _controller(controller)
  190. , _scheme(std::move(scheme))
  191. , _scroll(this, st::passportPanelScroll)
  192. , _done(
  193. this,
  194. tr::lng_passport_save_value(),
  195. st::passportPanelSaveValue) {
  196. setupControls(
  197. &error,
  198. &data,
  199. &scansError,
  200. &scansData,
  201. std::move(scans),
  202. std::move(translations),
  203. std::move(specialFiles));
  204. }
  205. PanelEditDocument::PanelEditDocument(
  206. QWidget*,
  207. not_null<PanelController*> controller,
  208. Scheme scheme,
  209. const QString &scansError,
  210. const ValueMap &scansData,
  211. ScanListData &&scans,
  212. std::optional<ScanListData> &&translations,
  213. std::map<FileType, ScanInfo> &&specialFiles)
  214. : _controller(controller)
  215. , _scheme(std::move(scheme))
  216. , _scroll(this, st::passportPanelScroll)
  217. , _done(
  218. this,
  219. tr::lng_passport_save_value(),
  220. st::passportPanelSaveValue) {
  221. setupControls(
  222. nullptr,
  223. nullptr,
  224. &scansError,
  225. &scansData,
  226. std::move(scans),
  227. std::move(translations),
  228. std::move(specialFiles));
  229. }
  230. PanelEditDocument::PanelEditDocument(
  231. QWidget*,
  232. not_null<PanelController*> controller,
  233. Scheme scheme,
  234. const QString &error,
  235. const ValueMap &data)
  236. : _controller(controller)
  237. , _scheme(std::move(scheme))
  238. , _scroll(this, st::passportPanelScroll)
  239. , _done(
  240. this,
  241. tr::lng_passport_save_value(),
  242. st::passportPanelSaveValue) {
  243. setupControls(&error, &data, nullptr, nullptr, {}, {}, {});
  244. }
  245. void PanelEditDocument::setupControls(
  246. const QString *error,
  247. const ValueMap *data,
  248. const QString *scansError,
  249. const ValueMap *scansData,
  250. ScanListData &&scans,
  251. std::optional<ScanListData> &&translations,
  252. std::map<FileType, ScanInfo> &&specialFiles) {
  253. const auto inner = setupContent(
  254. error,
  255. data,
  256. scansError,
  257. scansData,
  258. std::move(scans),
  259. std::move(translations),
  260. std::move(specialFiles));
  261. Ui::SetupShadowsToScrollContent(this, _scroll, inner->heightValue());
  262. _done->addClickHandler([=] {
  263. crl::on_main(this, [=] {
  264. save();
  265. });
  266. });
  267. }
  268. not_null<Ui::RpWidget*> PanelEditDocument::setupContent(
  269. const QString *error,
  270. const ValueMap *data,
  271. const QString *scansError,
  272. const ValueMap *scansData,
  273. ScanListData &&scans,
  274. std::optional<ScanListData> &&translations,
  275. std::map<FileType, ScanInfo> &&specialFiles) {
  276. const auto inner = _scroll->setOwnedWidget(
  277. object_ptr<Ui::VerticalLayout>(this));
  278. _scroll->widthValue(
  279. ) | rpl::start_with_next([=](int width) {
  280. inner->resizeToWidth(width);
  281. }, inner->lifetime());
  282. if (!specialFiles.empty()) {
  283. _editScans = inner->add(
  284. object_ptr<EditScans>(
  285. inner,
  286. _controller,
  287. _scheme.scansHeader,
  288. *scansError,
  289. std::move(specialFiles),
  290. std::move(translations)));
  291. } else if (scansData) {
  292. _editScans = inner->add(
  293. object_ptr<EditScans>(
  294. inner,
  295. _controller,
  296. _scheme.scansHeader,
  297. *scansError,
  298. std::move(scans),
  299. std::move(translations)));
  300. }
  301. const auto enumerateRows = [&](auto &&callback) {
  302. for (auto i = 0, count = int(_scheme.rows.size()); i != count; ++i) {
  303. const auto &row = _scheme.rows[i];
  304. Assert(row.valueClass != Scheme::ValueClass::Additional
  305. || !_scheme.additionalDependencyKey.isEmpty());
  306. auto fields = (row.valueClass == Scheme::ValueClass::Scans)
  307. ? scansData
  308. : data;
  309. if (!fields) {
  310. continue;
  311. }
  312. callback(i, row, *fields);
  313. }
  314. };
  315. auto maxLabelWidth = 0;
  316. enumerateRows([&](
  317. int i,
  318. const EditDocumentScheme::Row &row,
  319. const ValueMap &fields) {
  320. accumulate_max(
  321. maxLabelWidth,
  322. Ui::PanelDetailsRow::LabelWidth(row.label));
  323. });
  324. if (maxLabelWidth > 0) {
  325. if (error && !error->isEmpty()) {
  326. _commonError = inner->add(
  327. object_ptr<Ui::SlideWrap<Ui::FlatLabel>>(
  328. inner,
  329. object_ptr<Ui::FlatLabel>(
  330. inner,
  331. *error,
  332. st::passportVerifyErrorLabel),
  333. st::passportValueErrorPadding));
  334. _commonError->toggle(true, anim::type::instant);
  335. }
  336. inner->add(
  337. object_ptr<Ui::FlatLabel>(
  338. inner,
  339. data ? _scheme.detailsHeader : _scheme.fieldsHeader,
  340. st::passportFormHeader),
  341. st::passportDetailsHeaderPadding);
  342. enumerateRows([&](
  343. int i,
  344. const Scheme::Row &row,
  345. const ValueMap &fields) {
  346. if (row.valueClass != Scheme::ValueClass::Additional) {
  347. createDetailsRow(inner, i, row, fields, maxLabelWidth);
  348. }
  349. });
  350. if (data && !_scheme.additionalDependencyKey.isEmpty()) {
  351. const auto row = findRow(_scheme.additionalDependencyKey);
  352. const auto wrap = inner->add(
  353. object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
  354. inner,
  355. object_ptr<Ui::VerticalLayout>(inner)));
  356. const auto added = wrap->entity();
  357. auto showIfError = false;
  358. enumerateRows([&](
  359. int i,
  360. const Scheme::Row &row,
  361. const ValueMap &fields) {
  362. if (row.valueClass != Scheme::ValueClass::Additional) {
  363. return;
  364. }
  365. const auto it = fields.fields.find(row.key);
  366. if (it == end(fields.fields)) {
  367. return;
  368. } else if (!it->second.error.isEmpty()) {
  369. showIfError = true;
  370. } else if (it->second.text.isEmpty()) {
  371. return;
  372. }
  373. const auto fallbackIt = fields.fields.find(
  374. row.additionalFallbackKey);
  375. if (fallbackIt != end(fields.fields)
  376. && fallbackIt->second.text != it->second.text) {
  377. showIfError = true;
  378. }
  379. });
  380. const auto shown = [=](const Scheme::CountryInfo &info) {
  381. using Result = Scheme::AdditionalVisibility;
  382. const auto value = _scheme.additionalShown(info);
  383. return (value == Result::Shown)
  384. || (value == Result::OnlyIfError && showIfError);
  385. };
  386. auto langValue = row->value(
  387. ) | rpl::map(
  388. _scheme.preferredLanguage
  389. ) | rpl::flatten_latest();
  390. auto title = rpl::duplicate(langValue) | rpl::filter(
  391. shown
  392. ) | rpl::map([=](const Scheme::CountryInfo &info) {
  393. return _scheme.additionalHeader(info);
  394. });
  395. const auto headerLabel = added->add(
  396. object_ptr<Ui::FlatLabel>(
  397. added,
  398. rpl::duplicate(title),
  399. st::passportFormHeader),
  400. st::passportNativeNameHeaderPadding);
  401. std::move(
  402. title
  403. ) | rpl::start_with_next([=] {
  404. const auto &padding = st::passportNativeNameHeaderPadding;
  405. const auto available = added->width()
  406. - padding.left()
  407. - padding.right();
  408. headerLabel->resizeToNaturalWidth(available);
  409. headerLabel->moveToLeft(
  410. padding.left(),
  411. padding.top(),
  412. available);
  413. }, headerLabel->lifetime());
  414. enumerateRows([&](
  415. int i,
  416. const Scheme::Row &row,
  417. const ValueMap &fields) {
  418. if (row.valueClass == Scheme::ValueClass::Additional) {
  419. createDetailsRow(added, i, row, fields, maxLabelWidth);
  420. }
  421. });
  422. auto description = rpl::duplicate(langValue) | rpl::filter(
  423. shown
  424. ) | rpl::map([=](const Scheme::CountryInfo &info) {
  425. return _scheme.additionalDescription(info);
  426. });
  427. added->add(
  428. object_ptr<Ui::DividerLabel>(
  429. added,
  430. object_ptr<Ui::FlatLabel>(
  431. added,
  432. std::move(description),
  433. st::boxDividerLabel),
  434. st::passportFormLabelPadding),
  435. st::passportNativeNameAboutMargin);
  436. wrap->toggleOn(rpl::duplicate(langValue) | rpl::map(shown));
  437. wrap->finishAnimating();
  438. std::move(langValue) | rpl::map(
  439. shown
  440. ) | rpl::start_with_next([=](bool visible) {
  441. _additionalShown = visible;
  442. }, lifetime());
  443. }
  444. inner->add(
  445. object_ptr<Ui::FixedHeightWidget>(inner, st::passportDetailsSkip));
  446. }
  447. if (auto text = _controller->deleteValueLabel()) {
  448. inner->add(
  449. object_ptr<Ui::SettingsButton>(
  450. inner,
  451. std::move(*text) | Ui::Text::ToUpper(),
  452. st::passportDeleteButton),
  453. st::passportUploadButtonPadding
  454. )->addClickHandler([=] {
  455. _controller->deleteValue();
  456. });
  457. }
  458. return inner;
  459. }
  460. void PanelEditDocument::createDetailsRow(
  461. not_null<Ui::VerticalLayout*> container,
  462. int i,
  463. const Scheme::Row &row,
  464. const ValueMap &fields,
  465. int maxLabelWidth) {
  466. const auto valueOrEmpty = [&](
  467. const ValueMap &values,
  468. const QString &key) {
  469. const auto &fields = values.fields;
  470. if (const auto i = fields.find(key); i != fields.end()) {
  471. return i->second;
  472. }
  473. return ValueField();
  474. };
  475. const auto current = valueOrEmpty(fields, row.key);
  476. const auto showBox = [controller = _controller](
  477. object_ptr<Ui::BoxContent> box) {
  478. controller->show(std::move(box));
  479. };
  480. const auto isoByPhone = Countries::Instance().countryISO2ByPhone(
  481. _controller->bot()->session().user()->phone());
  482. const auto &[it, ok] = _details.emplace(
  483. i,
  484. container->add(Ui::PanelDetailsRow::Create(
  485. container,
  486. showBox,
  487. isoByPhone,
  488. row.inputType,
  489. row.label,
  490. maxLabelWidth,
  491. current.text,
  492. current.error,
  493. row.lengthLimit)));
  494. const bool details = (row.valueClass != Scheme::ValueClass::Scans);
  495. it->second->value(
  496. ) | rpl::skip(1) | rpl::start_with_next([=] {
  497. if (details) {
  498. _fieldsChanged = true;
  499. updateCommonError();
  500. } else {
  501. Assert(_editScans != nullptr);
  502. _editScans->scanFieldsChanged(true);
  503. }
  504. }, it->second->lifetime());
  505. }
  506. not_null<Ui::PanelDetailsRow*> PanelEditDocument::findRow(
  507. const QString &key) const {
  508. for (auto i = 0, count = int(_scheme.rows.size()); i != count; ++i) {
  509. const auto &row = _scheme.rows[i];
  510. if (row.key == key) {
  511. const auto it = _details.find(i);
  512. Assert(it != end(_details));
  513. return it->second.data();
  514. }
  515. }
  516. Unexpected("Row not found in PanelEditDocument::findRow.");
  517. }
  518. void PanelEditDocument::updateCommonError() {
  519. if (_commonError) {
  520. _commonError->toggle(!_fieldsChanged, anim::type::normal);
  521. }
  522. }
  523. void PanelEditDocument::focusInEvent(QFocusEvent *e) {
  524. crl::on_main(this, [=] {
  525. for (const auto &[index, row] : _details) {
  526. if (row->setFocusFast()) {
  527. return;
  528. }
  529. }
  530. });
  531. }
  532. void PanelEditDocument::resizeEvent(QResizeEvent *e) {
  533. updateControlsGeometry();
  534. }
  535. bool PanelEditDocument::hasUnsavedChanges() const {
  536. const auto result = collect();
  537. return _controller->editScopeChanged(result.data, result.filesData);
  538. }
  539. void PanelEditDocument::updateControlsGeometry() {
  540. const auto submitTop = height() - _done->height();
  541. _scroll->setGeometry(0, 0, width(), submitTop);
  542. _done->resizeToWidth(width());
  543. _done->moveToLeft(0, submitTop);
  544. _scroll->updateBars();
  545. }
  546. PanelEditDocument::Result PanelEditDocument::collect() const {
  547. auto result = Result();
  548. for (const auto &[i, field] : _details) {
  549. const auto &row = _scheme.rows[i];
  550. auto &fields = (row.valueClass == Scheme::ValueClass::Scans)
  551. ? result.filesData
  552. : result.data;
  553. if (row.valueClass == Scheme::ValueClass::Additional
  554. && !_additionalShown) {
  555. continue;
  556. }
  557. fields.fields[row.key].text = field->valueCurrent();
  558. }
  559. if (!_additionalShown) {
  560. fillAdditionalFromFallbacks(result);
  561. }
  562. return result;
  563. }
  564. void PanelEditDocument::fillAdditionalFromFallbacks(Result &result) const {
  565. for (const auto &row : _scheme.rows) {
  566. if (row.valueClass != Scheme::ValueClass::Additional) {
  567. continue;
  568. }
  569. Assert(!row.additionalFallbackKey.isEmpty());
  570. auto &fields = result.data;
  571. const auto j = fields.fields.find(row.additionalFallbackKey);
  572. Assert(j != end(fields.fields));
  573. fields.fields[row.key] = j->second;
  574. }
  575. }
  576. bool PanelEditDocument::validate() {
  577. auto error = _editScans
  578. ? _editScans->validateGetErrorTop()
  579. : std::nullopt;
  580. if (error) {
  581. const auto errortop = _editScans->mapToGlobal(QPoint(0, *error));
  582. const auto scrolltop = _scroll->mapToGlobal(QPoint(0, 0));
  583. const auto scrolldelta = errortop.y() - scrolltop.y();
  584. _scroll->scrollToY(_scroll->scrollTop() + scrolldelta);
  585. } else if (_commonError && !_fieldsChanged) {
  586. const auto firsttop = _commonError->mapToGlobal(QPoint(0, 0));
  587. const auto scrolltop = _scroll->mapToGlobal(QPoint(0, 0));
  588. const auto scrolldelta = firsttop.y() - scrolltop.y();
  589. _scroll->scrollToY(_scroll->scrollTop() + scrolldelta);
  590. error = firsttop.y();
  591. }
  592. auto first = QPointer<Ui::PanelDetailsRow>();
  593. for (const auto &[i, field] : ranges::views::reverse(_details)) {
  594. const auto &row = _scheme.rows[i];
  595. if (row.valueClass == Scheme::ValueClass::Additional
  596. && !_additionalShown) {
  597. continue;
  598. }
  599. if (field->errorShown()) {
  600. field->showError();
  601. first = field;
  602. } else if (row.error) {
  603. if (const auto error = row.error(field->valueCurrent())) {
  604. field->showError(error);
  605. first = field;
  606. }
  607. }
  608. }
  609. if (error) {
  610. return false;
  611. } else if (!first) {
  612. return true;
  613. }
  614. const auto firsttop = first->mapToGlobal(QPoint(0, 0));
  615. const auto scrolltop = _scroll->mapToGlobal(QPoint(0, 0));
  616. const auto scrolldelta = firsttop.y() - scrolltop.y();
  617. _scroll->scrollToY(_scroll->scrollTop() + scrolldelta);
  618. return false;
  619. }
  620. void PanelEditDocument::save() {
  621. if (!validate()) {
  622. return;
  623. }
  624. auto result = collect();
  625. _controller->saveScope(
  626. std::move(result.data),
  627. std::move(result.filesData));
  628. }
  629. object_ptr<Ui::BoxContent> RequestIdentityType(
  630. Fn<void(int index)> submit,
  631. std::vector<QString> labels) {
  632. return Box<RequestTypeBox>(
  633. tr::lng_passport_identity_title(),
  634. tr::lng_passport_identity_about(tr::now),
  635. std::move(labels),
  636. submit);
  637. }
  638. object_ptr<Ui::BoxContent> RequestAddressType(
  639. Fn<void(int index)> submit,
  640. std::vector<QString> labels) {
  641. return Box<RequestTypeBox>(
  642. tr::lng_passport_address_title(),
  643. tr::lng_passport_address_about(tr::now),
  644. std::move(labels),
  645. submit);
  646. }
  647. object_ptr<Ui::BoxContent> ConfirmDeleteDocument(
  648. Fn<void(bool withDetails)> submit,
  649. const QString &text,
  650. const QString &detailsCheckbox) {
  651. return Box<DeleteDocumentBox>(text, detailsCheckbox, submit);
  652. }
  653. } // namespace Passport