=+O0;D}DC%{4;v(bb4jR`nEnQ^I>NO~uE`rw)VEv%)T!GTFDomN%IEIx?lpgxzt0Fz
z6LH;q3hl-zn;=6lic~dr_X#=@Uj`?(-G@^ri?k1%87YBNH~??aa@+sIP&7klQ;H&b
z`^_Z=PlYHE&6gqi8l
z(5!yG@)np<9rIKh{LQ?*9?%$JyYwHw%9NtnrAN&>N{3m-qrPwmM2na8_pp1VUXSf1
z)huUsM*`Nze`!zdNa6;LVal0_FdcnBM4HQV63|ZL-o{5?Gsp^mEX%#s*k@Y<>`vfN
z*g51lIQZ^1KZkz6%KnqIx|UNa{euTm^MjHc^0v}ePQrCTtE9gAFZ8i(PEI&!l6t*7
znF7j%*r6m)sp-F%dR+YZm0rVW#M7I-e?3n%H8t{^>*Yo`hMI6S0<|nOG0BCsGJIhW
zg^nXWIy!2N2!8DJU(Wz${KpUXHuPUGqTz?DX*G*ch7X*YZbii`76jR6-rm~!PrF=E
zZR#_eqt1@_v^+2sKHv7tCr%&)4yiQ2o_iA%FtES}YbC^}m
z)@ztOhr`moC(Yr#ow2d;--Z3x9oqm2UNft*R3CXLZ^?GWDQAgm7}9tV^d!)d*hL}q
zjUs=9f}BS1-#pd6wqU<`YwcWt(M_U00Dor$tPvWW<*Ep*0mo^V*FGLBd=GOX-x%qn
zwmTJ;aa@=q_tVkK#hid9htpvt`;v6X#2j|3
zZj2WFvo(PQ;f2w$o{G<`_#Y~*mQFa#lhbTJJ_G$jn_e2ifi|1x{mVOTe@L;I6`&mZ
z(b=&)gK0B=)j1FP^wT4iB~0~{MbNN{ooBWVD`jb++01k5fxV?6xeeL4ICl4@hVTW$
zOhEORCH6lZ!>z0$c=S*e$Y1R4S}~!6H(|r3KLTwEF`>57Z+v|4-(FkskKy@mm{Igh
z)XYO&kZK}{pGWAdejY!pxaFA)e!DmE^Jn!RW!5+@D#)%!oQ;Qg)Wsdm;jPHPc-#>2aVLc7h`o}CJ9EokHME`9I{h63(|6G
z>qg=hP>^NBsAqD%PRF|Y#l6vm8_%6wpL%T<_s?Iy&biszS>cqkS?8ls)3HQAiq(4<
z9|-dZV|*DPsbH(9+VX(nv+9ptfu41WsMGAFfik$veEhCFz|topsjkiA+xENG2QPv+
zoZ|SZtR#1+_@A#b-X$JB*a+mPou!T=<;lJ~dyJ$d&+3Loqcn^6lHVwPe(9PCO=Itc~N(Z{b
z!?Q2cx+yl_YHCEsUIx7irPC%*Q&9;&i|@4vR_WUN7}s#Qly<_v{<~AjNN^K2*sb%2
z9OVCeNn8*LopQCaDZ=pUZAg2HgR!>u@ci?aVUw!H;w*b1
zNe37?LCHD25*9k(RG8_me*VQ$F;2CrlZaZJKrO^9JA4KEtO`NRmN`
z-Uk;fT4`f>uN0!_`IdGk_%_!iCu4DC<+E)C)T>NX0b6acJhiHjronznDY5YDscc47TKlb0P#F~>fBOu7<-m~HL8rFsb6
z;IOm`azF0o+w@e13;Fv!O2en?zhcXCP`_Ej{4CJ)QM9V1B4aFWXt?kdW+X6rBAXQB
z+$H6{HuLM(bMnIp)e=U!_PcTb&}aVAlJb1THnfzYEI_>HcqS^Uk0T(+zq&UKNkJ8-
z@#TXnbk4PL@>J<6Ne%Rnm-otb_N62;fq=mN(drj;{>OL0X>5m?RF000{@X09oRjbk
z>Y}JgfLzioFqy7=)WDANb$aUIgS1X{1Zu(=H3i-A@sFuHf@ZgC>+5at$`ti~`}LK3
zdU^uK$Hs*ECt$DT?Q-BP7HR)o;(4fi&oq~}-JyI4&3m?C1^^CRMs_7!UyMB7zp${l
zYr(kc{Hvbh2uFq!SgV6IG&EYd?Xf}5$7a0WNT-9{H@CO_iIal
z6~49PHdfUMsU-Rl_+>HxE}b1ZaaLoVs+Vi0cwG6SS6oP#wE6#Eu7E)6Ko}}_PwC_8
z(!R4IJR7iI!s6q+re1t?3dmQ!*fIF%b{*-t1HO5mnEhnj!-@sM$S%I*v2Qum+jMGP
z_;vO5J}!LhonpN>wnS8)h`Dgwu@V052M2C?fOFe=XJ{#Qy2rHoHNWVl8%wP3V9VxPEx||QGaVn=bM(qZ=V=bjf+j~Mp`bEvM0%Z>!n%_y?G}DQZOF$zLwt}q7l#qtJvG#gxI2<2baik
zN)x~T;G=o7hGXyK@I_}|99Ct=-W~+02}X?=w@190^5*-i{*C#TJGo_))>G!U8-W4G
zDu7+};MehHp29Led}uf_judIr1X0n^;sosA+b^yI4eMo#qF*KsH%m=r_2ZpvP8@g7
zkFUcC`iGU2l)Q(Bhm(D4M!(!SmCO&TJb}?;zt`-rn%ubux>-I00Za1WkXLBTZl&dC
zyqIIoo0s2P^mmo!$GBzZ?Q@A&I)g5U`C@c(@|aV#c*#TVXYhPW0JFAg7VO?B>N@Ur
zf?C~ABX%xVL#z53?_Cr31qmX%UjIWFy1Bo4Scz_y1~2?^M|_c@Jl8|y#Zm_eRp
z+xJ}E0`$-akyg3sHc#j!tA-ME?`J7>_+QFqohB4a2gXL9Q1DHv6RclzaT4NQWBD>&
zby-4T!+_~LmofZw6<;&&x9eZ!b*54$xSMiQURY=!1C9uk4DP2EoHHWICeB$y4(tb@
zvd%+YmIVl0X=>c@2b$yxciO2_X{XtRqR3RaFLoy;jMQCS_vvxN+1nM0SQft;Q0Hs3
zw%oOqmkWppH8(ZElIb5G&i%{kT1|y}5(CW5T#sX`!Nhu!E`{H)C=Hzf4AeI9#!HUvHsq1|KnydZ(G>
zdk0(K%vf?^V8G#U@ez`B_U^3
zN*EOss1>vOlY^Qs^EF|!OG``AoR4FPqPVVS*%llZ5-Ikl5B*p-{+v@c##r625Z#&_
z1RO9FYb
zTFRq1%TICas8Hnd-Mp`e4V7TZVPZgGe1vKF->S0L=-EQD)%T6fp+SK?{yTS|&*H
zExDJdyp*+OqLKY1pXCISsLO<%rz$->Nh!cty=
zUp&e6*9rUA%-(A8zZ(oj5&$z6Bqvx(+I02mzyGd+5dZ1r0ciUfblvl#trjHnom?1h
z@C-9m)c1dBvq@{f<;aouKnNAAHJmah2UotQtNthz5Y!U5R6-ZZvS%Ga7TJX#^+g3Y
z+pM?bKba^YUXS^2um6s`rIZEQwv9OR3Urp5BM&cSE<~d5S$o&bFK*;8Q^zWJ6L+x#
z5xR1mFtg;eG;fKzM&RC11(=DKhiAc*fg>y^^}^*fKt{ZMch4^;&)%$!eLc6p!`Iok
z6+RzC*DUL#$`TwhnuEEc4XgBgkzZ+ylG=@Tb^#48ZcF5pw4W~@Q{5-#Wvg85zMVMo
z8YQm#PCEVcoAd?GhSNFS;iW@>MewNZKshb#7i_r_9MQ2>XyjY8S#s_)=JzL@t(OHH
z(6~Y1Vv_y$*Yy37=R?YKuZ#=~XxWzfsrHi@xf-jNmn|=l0BXRg&XHH9px78zMJHMg
z50MUi(ot^kN3+9xIGSlT7>O->kVDdb)W6Be#=gx$ZA
zsz7epztdu*^+>}CDqqS0)e@Sx7e~0@B}oW4R9lH4Bx7_Kx1?RV(pc&p^c6%N_73^{
z@o9!iA~V(10?_HTqqb4`CXZl+RZf%D=E+Q&8DkCcM}IeqQu$K}n*b9MMIatj3g61`
zxNsvxm*-eHhs={C=08EdI8Xky3IdmQ*))&_AO|)RElZB99U@SgrD~AsZeeeW+0o!ud#gYPBwoJK({{(2y66G;wS`Hr`)#rpHyx
z^Td)8LOcdBUsMQTJY-hl_urHHzt+{AfrZ7fi|@H+U~l9d^x15JigdLC&BMC@UU$4AGH
zlfw4j=;#=U?UIp`>k^AxSw>`{c&4koTt44~$;YiSAVk2~f8m#YWi`JHQ|^D!iJXb2
z%s+qikR4F&v$-iF`84^Ms=7wm?HF}ad8gh}@87hvgbMENhqx|;W5kkBAvo1tt%-bY
zP!;wpc}Dy7^|d_Tnmrcb0T@R7O*S>+?HogY+hy|ykhJqJL6{{0fgzqK<&&v3-y)P-
zkX&c@C@sgOc#d!nU=6fXto4y!orEbs%2v`tC7B-sJxurhPE8HG#A8L%U#^H&`HI2@
z6^{TmMHy7bvV1o{I~ev#h51AOz`zw=UFHk#!l0S*iV8uhJ1y3xu0RG}u`LGkGDaIZ
zM!~!C?5{lyM?qH#$?z;)>QoA$%ib9LdwXO4(9wJLOBo?9tC4DYK8j@4sD2(eocH5x
zOlz@2OmbZ5Td5FF>G3rnY7qKk<%g@xpg=cN4k8CiWOyXiD*j}g($d%F=&Xn=>{P^TXaWW=WaffpaT1HrzV|E@jI_^H;fu4#aZ&Yd*$)M^`e%q
z&a|3V5KS{D>-y2_gY$*&2dU)n0HQbzov1GS#woqkBPw+-@WL-lTg>fTq^=yVJ$b-U
zaQr$dh>4N0*%D9%a;?<}6kKuLKf1p#ZicoKFKwulxdIxL8wuZhW}%O{-;~dpJ*7)J
zH3C1~>QycsfXtWvc7^7wcJGc;{UE1JE!%gOaayIuUEN<;u#&C&&LBf^1M@YPU@Et*
zPGrVEVP&5i3FIs+0=b*7#&Mq^vFmie@}UweVWXeh`G9u90Uo3OOjB0d+vvLA0sac^
z-~KQWyc{W`92~azmcTwQIOFzm1ZO8*3#xXd8!c%FbsvEwrCYq+eE!cswcg-X^zL|}
zrX>cgZ)fjj^QfG2scRDFF8h@vl9cr`;Id1F
z$Yp7M|GcS*$y>A$2c8TV>8YQlU>emca_Oi;jWr@>jcmLJ>dxl}9jBzJK(6-p@#DvF
z(%aq0i8kv!sY#~?Rba5?@oEu|5DQvHX2r?#3Y(%%SG-a!&RQ9`6UvU1yYRO8^~?Ty6vNkrx6
zys9VUyeXw
zD%C7eDika$B!RfS-a{U>>>0>b+Krykfb$g2*%w2)SQ$YdO
zmXJC`#2D&6&UVRvk-43OL*Bt(t6ij1rVH#Xe#*Q*W~U7PQ>G)sjR$H$MhY}%s(!t(
zrDZ&I*?(DTz`Rt3U%!|qK=5M40yib7^nQ_ey5m$v$>0ClrnVp@`xorC$eaK>JU2qI
zQwdeQP0~Khm<3j3EQ>vsCi(^N#;mF4V74ff6&H@r-E&o#ix$qOl>318yC*t~ag2z7
zlp7`x;vi-!A_v&gAAW*8nB7^tN{i5IjHCnjn
zIO2(LmK*diD-35!+cM-&*r(KR|GxibJi1Uq^89k?Rs|7oVpDoM9#xcWBf+ULz1
z+My@dKAk`S!mP_$$I*XJtqJ^=gBeXl74K%*DmvUaP(i@z+oeD^DDotI+EZS<`6dey
z)%LYpmEh>6
zjK>D>2HEa(=k_;tvsuDx+|w!Nm2ruQmOS?S{3)zJMDeNVD(!&0W8!3zLHjv({Jd>x
zWWlut%h=)_NvdXP`7&*cDD0-sv;j#o-O}P^aHIlmes1o9l9t4O@-QgpP_cC1EROQ*
zE7c>{Ikmq1jIvkc>TgCg?bz6J=ouLL7yValQa`4iMD|0RC6{l2ojGuga>1}VEESlS
zS3?p{T@)X)h7b^(MN>jfdaDXF)34r+V_}wDN65
zfplV$pBvACmt1reJyuzqLKujZxm{E^Gb&i
z0I8T3=HGvk0rk80sCy-XJ!tD+UJDa)O!BAaaP;-{MM*`ENQsGy%j2`UdLQ^KU@zo!
z^mZWa&W$_9Y3ig!naI))5***}QsbxuM!sR{`2PKS4j2r!=R-xI
z!~1L=-_~a|iUz{D>sQgK*b*@=$B9c~$I2$}559Ww+kCC|$Jm%#8Q=#Prz=jW{C3Ib~|9&IcF5Y7Dd2n`*zULgBfKZDW~>JjI69}8Zl=ioJFbTI4g`t$7OKJ
zKmjpArS?EJc~#WaQ}Z4^_21`qEuJ9tU7p|9Ttf*Q>GM4>mGb|fQ+l6b?#%_4S6u;c
z{d`Qfh}k99ShnKi$Xk$LszG-$SNomtetg)g*!nEH8fPBgqvMvop`jUa;e@8(y6pGQ2|ll?09Hss5~~V
ztPhfZ)MHjqzgcf2#Oh|p?(Xg^?iXtZZCUGO;-CPV6Ka3f9Z5TCh2{oqyMo&~_k;f(
zn6!AhLJ%?kqq9Awo{jSJG$3xV_knaGh94pk1J}FVEi6(Rm_5D5WDuJ~Op$gupgAZx
zNSRdTBDdl8q-mdMk9BQoYJzr*|42gM8xVpS0)iwI^<-El($mv-hk&ki?!&(!Vl}ZR
z?jf8r1O!R-a{1XfnE0>~$z?fC-;f-ymrWu-LZ03F0}gfHXHZ-^eyl`;nZypxkF;;~NXC_>Mx|#+-dk`pAqIbitRXJJHb(
zI^`z|i)|Aa_#Z|B>4+VGsYWI?*0HOriw{ctZ>fGDAmKzk3yXhartNy?HC9!h1}m9J
zoymv3VD&NMhC4R_L3wdK2@CrdIuk$Z10SLU1R^%$VT$ne!}4ZSi-dI|csl;R9()DgPdLScWb$omYg>o7&$t6T6bc;~Du0oobjf;&d$zkXKd8As89gIIhj
zBO~Jj`1>gEUetTUxewcj<-^viVm1m8w@lTJ{Df~^@{@(dwhhk8k<xv8Q7iiLMhbipqiSR5$Cq7H!pxdxpN+h_MH`GC>xpNtaZEC7DQ{={u8POG^u{vU(m#&FG`&ArhCyCMuH2ie>YYemLXD@0fIvpm^Ev
zSz>Aikf?pXk&w1oUnIEa#SD+_pFmNgpY(~JPCET`mcg>OY}pd^cohP+pW*guTP9m1
z`xyd4pby>UoY|8s6Lo8ITL~$orKRodA|Re0fsVF5kI&*_SsVS07IN+RvX0eh!GI20*@hx$fLGy4`
zphuU_2h3)#U~J}#3x8*_qEGzn?(Xh_Pf^cTiUnrJXE2%hi8;4r8JJc7UCi)am0P{q
z;Wlh%Zr|G4Is_kQyz3mv^BL6_SQlAw9VI_U0ext9B=R=3y|T|)KUP9fmn}PiRngwG
zs}elh69VG;&W+gs5xEL|cY^IhJ8tz=bk-Z9$;Z#n`Ph8;|CX}JPl}xt5DN!dwV-C&0PiGeZPmo50E&oX7XU-Az8kY>U$;`%xZa`
z0F%?-kZe9ECT0ba)M>QAdZ|P@7r70|HbV0Ko0#D_i(fKf(+{>C?ZuMcE1zp?Yo`VV
z2KeT_sGH{$VEY+mho7u}K|#T+&;RP|?7Yrd548`(_Yv7-1|U+A6^JT_b;5VP1cYGU
z?eFhzvs6%Y%HS%AT@JqFepV`F2v
zyt9_?q~^d=gNO+%HmS#(EK?-_`oNVE5??#~oePNh;bJ=or>_LV-{-)O%HH1Ik&cdz$l*Lr2KerC
ze)4~Qzjf=@A&71cw*N6W>!U~T+qGb9c^{XbX8`r!t8!zvCue=sJAQjl%(mnm^0P0X
z{C8pBpN7NXwOw6Z6RQ3s@5IMy5Y-+pz&00QTThQY!M68^Pt(M{RzNH)<^u-Hf#97;
zkZ@Od0QKgHi=)0743xi|PVRd?`6&^{U0!eSwSf%s7jzzf00Fw0%n*Vi}7
zq|VtbFPCNFr>HRL2O;;Rkkr-HMb2kn2FXXQH>124z#BkM_Y_!ISXfvrazIda1`k7G
zKNtKt_#pVw#Kc4hE0(vgT4?R>?;qT{bt_-=6Gc8l2;Yx$8*E#ReRx=bhX?Kjd06EH
l*f#cHMhlCD!#m#b{{c0^ywULyI%)s_002ovPDHLkV1le_^4 books;
+ private string searchQuery;
+ private string bookCount;
+ private bool isBookGridVisible;
+ private bool isSearchProgressPanelVisible;
+ private string searchProgressStatus;
+ private bool isStatusBarVisible;
+
+ public FictionSearchResultsTabViewModel(MainModel mainModel, IWindowContext mainWindowContext, string searchQuery, ObservableCollection searchResults)
+ : base(mainModel, mainWindowContext, searchQuery)
+ {
+ columnSettings = mainModel.AppSettings.Fiction.Columns;
+ this.searchQuery = searchQuery;
+ books = searchResults;
+ OpenDetailsCommand = new Command(param => OpenDetails(param as FictionBook));
+ SearchCommand = new Command(Search);
+ BookDataGridEnterKeyCommand = new Command(BookDataGridEnterKeyPressed);
+ Initialize();
+ }
+
+ public string SearchQuery
+ {
+ get
+ {
+ return searchQuery;
+ }
+ set
+ {
+ searchQuery = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ public ObservableCollection Books
+ {
+ get
+ {
+ return books;
+ }
+ set
+ {
+ books = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ public bool IsBookGridVisible
+ {
+ get
+ {
+ return isBookGridVisible;
+ }
+ set
+ {
+ isBookGridVisible = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ public int TitleColumnWidth
+ {
+ get
+ {
+ return columnSettings.TitleColumnWidth;
+ }
+ set
+ {
+ columnSettings.TitleColumnWidth = value;
+ }
+ }
+
+ public int AuthorsColumnWidth
+ {
+ get
+ {
+ return columnSettings.AuthorsColumnWidth;
+ }
+ set
+ {
+ columnSettings.AuthorsColumnWidth = value;
+ }
+ }
+
+ public int SeriesColumnWidth
+ {
+ get
+ {
+ return columnSettings.SeriesColumnWidth;
+ }
+ set
+ {
+ columnSettings.SeriesColumnWidth = value;
+ }
+ }
+
+ public int YearColumnWidth
+ {
+ get
+ {
+ return columnSettings.YearColumnWidth;
+ }
+ set
+ {
+ columnSettings.YearColumnWidth = value;
+ }
+ }
+
+ public int PublisherColumnWidth
+ {
+ get
+ {
+ return columnSettings.PublisherColumnWidth;
+ }
+ set
+ {
+ columnSettings.PublisherColumnWidth = value;
+ }
+ }
+
+ public int FormatColumnWidth
+ {
+ get
+ {
+ return columnSettings.FormatColumnWidth;
+ }
+ set
+ {
+ columnSettings.FormatColumnWidth = value;
+ }
+ }
+
+ public int FileSizeColumnWidth
+ {
+ get
+ {
+ return columnSettings.FileSizeColumnWidth;
+ }
+ set
+ {
+ columnSettings.FileSizeColumnWidth = value;
+ }
+ }
+
+ public bool IsSearchProgressPanelVisible
+ {
+ get
+ {
+ return isSearchProgressPanelVisible;
+ }
+ set
+ {
+ isSearchProgressPanelVisible = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ public string SearchProgressStatus
+ {
+ get
+ {
+ return searchProgressStatus;
+ }
+ set
+ {
+ searchProgressStatus = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ public bool IsStatusBarVisible
+ {
+ get
+ {
+ return isStatusBarVisible;
+ }
+ set
+ {
+ isStatusBarVisible = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ public string BookCount
+ {
+ get
+ {
+ return bookCount;
+ }
+ set
+ {
+ bookCount = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ public FictionBook SelectedBook { get; set; }
+
+ public Command OpenDetailsCommand { get; }
+ public Command SearchCommand { get; }
+ public Command BookDataGridEnterKeyCommand { get; }
+
+ private void Initialize()
+ {
+ isBookGridVisible = true;
+ isStatusBarVisible = true;
+ isSearchProgressPanelVisible = false;
+ UpdateBookCount();
+ Events.RaiseEvent(ViewModelEvent.RegisteredEventId.FOCUS_SEARCH_TEXT_BOX);
+ }
+
+ private void UpdateBookCount()
+ {
+ BookCount = $"Найдено книг: {Books.Count.ToFormattedString()}";
+ }
+
+ private void BookDataGridEnterKeyPressed()
+ {
+ OpenDetails(SelectedBook);
+ }
+
+ private void OpenDetails(FictionBook book)
+ {
+ FictionDetailsWindowViewModel detailsWindowViewModel = new FictionDetailsWindowViewModel(MainModel, book);
+ IWindowContext detailsWindowContext = WindowManager.CreateWindow(RegisteredWindows.WindowKey.FICTION_DETAILS_WINDOW, detailsWindowViewModel, MainWindowContext);
+ FictionDetailsWindowSettings detailsWindowSettings = MainModel.AppSettings.Fiction.DetailsWindow;
+ detailsWindowContext.ShowDialog(detailsWindowSettings.Width, detailsWindowSettings.Height);
+ }
+
+ private async void Search()
+ {
+ if (!String.IsNullOrWhiteSpace(SearchQuery) && !IsSearchProgressPanelVisible)
+ {
+ Title = SearchQuery;
+ IsBookGridVisible = false;
+ IsStatusBarVisible = false;
+ IsSearchProgressPanelVisible = true;
+ UpdateSearchProgressStatus(0);
+ Progress searchProgressHandler = new Progress(HandleSearchProgress);
+ CancellationToken cancellationToken = new CancellationToken();
+ ObservableCollection result = new ObservableCollection();
+ try
+ {
+ result = await MainModel.SearchFictionAsync(SearchQuery, searchProgressHandler, cancellationToken);
+ }
+ catch (Exception exception)
+ {
+ ShowErrorWindow(exception);
+ }
+ Books = result;
+ UpdateBookCount();
+ IsSearchProgressPanelVisible = false;
+ IsBookGridVisible = true;
+ IsStatusBarVisible = true;
+ }
+ }
+
+ private void HandleSearchProgress(SearchProgress searchProgress)
+ {
+ UpdateSearchProgressStatus(searchProgress.ItemsFound);
+ }
+
+ private void UpdateSearchProgressStatus(int booksFound)
+ {
+ SearchProgressStatus = $"Найдено книг: {booksFound.ToFormattedString()}";
+ }
+ }
+}
diff --git a/ViewModels/ImportWindowViewModel.cs b/ViewModels/ImportWindowViewModel.cs
new file mode 100644
index 0000000..214f550
--- /dev/null
+++ b/ViewModels/ImportWindowViewModel.cs
@@ -0,0 +1,396 @@
+using System;
+using System.Collections.ObjectModel;
+using System.Threading;
+using LibgenDesktop.Infrastructure;
+using LibgenDesktop.Models;
+using LibgenDesktop.Models.ProgressArgs;
+using LibgenDesktop.Models.SqlDump;
+using LibgenDesktop.Models.Utils;
+
+namespace LibgenDesktop.ViewModels
+{
+ internal class ImportWindowViewModel : ViewModel
+ {
+ internal class LogLine : ViewModel
+ {
+ private string step;
+ private string header;
+ private string status;
+
+ public LogLine(string step, string header, string status)
+ {
+ this.step = step;
+ this.header = header;
+ this.status = status;
+ }
+
+ public string Step
+ {
+ get
+ {
+ return step;
+ }
+ set
+ {
+ step = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ public string Header
+ {
+ get
+ {
+ return header;
+ }
+ set
+ {
+ header = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ public string Status
+ {
+ get
+ {
+ return status;
+ }
+ set
+ {
+ status = value;
+ NotifyPropertyChanged();
+ }
+ }
+ }
+
+ private readonly MainModel mainModel;
+ private readonly string dumpFilePath;
+ private readonly CancellationTokenSource cancellationTokenSource;
+ private bool isInProgress;
+ private string status;
+ private ObservableCollection logs;
+ private string resultLogLine;
+ private bool isResultLogLineVisible;
+ private string errorLogLine;
+ private bool isErrorLogLineVisible;
+ private string elapsed;
+ private bool isCancellationEnabled;
+ private bool isCancelButtonVisible;
+ private bool isCloseButtonVisible;
+ private decimal lastScannedPercentage;
+ private TableType tableType;
+ private DateTime startDateTime;
+ private TimeSpan lastElapsedTime;
+
+ public ImportWindowViewModel(MainModel mainModel, string dumpFilePath)
+ {
+ this.mainModel = mainModel;
+ this.dumpFilePath = dumpFilePath;
+ cancellationTokenSource = new CancellationTokenSource();
+ CancelCommand = new Command(Cancel);
+ CloseCommand = new Command(Close);
+ Initialize();
+ }
+
+ public bool IsInProgress
+ {
+ get
+ {
+ return isInProgress;
+ }
+ set
+ {
+ isInProgress = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ public string Status
+ {
+ get
+ {
+ return status;
+ }
+ set
+ {
+ status = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ public ObservableCollection Logs
+ {
+ get
+ {
+ return logs;
+ }
+ set
+ {
+ logs = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ public string ResultLogLine
+ {
+ get
+ {
+ return resultLogLine;
+ }
+ set
+ {
+ resultLogLine = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ public bool IsResultLogLineVisible
+ {
+ get
+ {
+ return isResultLogLineVisible;
+ }
+ set
+ {
+ isResultLogLineVisible = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ public string ErrorLogLine
+ {
+ get
+ {
+ return errorLogLine;
+ }
+ set
+ {
+ errorLogLine = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ public bool IsErrorLogLineVisible
+ {
+ get
+ {
+ return isErrorLogLineVisible;
+ }
+ set
+ {
+ isErrorLogLineVisible = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ public string Elapsed
+ {
+ get
+ {
+ return elapsed;
+ }
+ set
+ {
+ elapsed = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ public bool IsCancellationEnabled
+ {
+ get
+ {
+ return isCancellationEnabled;
+ }
+ set
+ {
+ isCancellationEnabled = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ public bool IsCancelButtonVisible
+ {
+ get
+ {
+ return isCancelButtonVisible;
+ }
+ set
+ {
+ isCancelButtonVisible = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ public bool IsCloseButtonVisible
+ {
+ get
+ {
+ return isCloseButtonVisible;
+ }
+ set
+ {
+ isCloseButtonVisible = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ public Command CancelCommand { get; }
+ public Command CloseCommand { get; }
+
+ private void Initialize()
+ {
+ isInProgress = true;
+ status = "Шаг 1 из 2. Поиск данных для импорта...";
+ logs = new ObservableCollection();
+ isResultLogLineVisible = false;
+ isErrorLogLineVisible = false;
+ startDateTime = DateTime.Now;
+ lastElapsedTime = TimeSpan.Zero;
+ elapsed = GetElapsedString(lastElapsedTime);
+ isCancellationEnabled = true;
+ isCancelButtonVisible = true;
+ isCloseButtonVisible = false;
+ lastScannedPercentage = 0;
+ LogLine searchHeaderLogLine = new LogLine("Шаг 1", "Поиск данных для импорта в файле", "Идет сканирование файла...");
+ logs.Add(searchHeaderLogLine);
+ Import();
+ }
+
+ private async void Import()
+ {
+ Progress