mirror of
https://github.com/wgtunnel/desktop.git
synced 2026-06-02 00:29:09 +02:00
feat: add tray tunnel/lockdown status indicator
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0px" y="0px" width="100%" viewBox="0 0 500 500" enable-background="new 0 0 500 500" xml:space="preserve" style="background: transparent;">
|
||||
|
||||
<path fill="#FDFDFD" opacity="1.000000" stroke="none" d=" M343.600830,328.661926 C336.364685,343.733398 329.901733,358.782043 322.205139,373.170807 C315.794189,385.156036 300.452118,392.714020 286.033417,386.615112 C278.383942,383.379486 272.853790,377.422882 269.419830,369.957458 C258.360992,345.915649 247.455307,321.801636 236.702835,297.621094 C227.138412,276.112305 217.782761,254.509644 208.474655,232.888275 C201.677689,217.099991 195.120636,201.208496 188.431564,185.373611 C184.111832,175.147583 179.699860,164.960342 175.412231,154.720978 C167.168060,135.032944 183.615067,116.238884 199.803223,116.928459 C211.526840,117.427849 219.589691,122.657906 224.396683,132.725189 C232.842834,150.413971 240.886108,168.300461 248.816269,186.228821 C256.720245,204.097977 264.289948,222.115158 271.984283,240.076828 C279.991425,258.768646 287.971436,277.472137 295.974182,296.165863 C296.095032,296.448151 296.356415,296.670319 296.872162,297.329437 C315.180756,254.966919 333.386810,212.841614 351.915741,169.969193 C336.988495,169.969193 323.198395,170.198090 309.420380,169.888458 C300.284088,169.683136 291.076721,171.237061 282.013672,168.092346 C270.647339,164.148422 262.275635,152.328369 263.931549,140.821854 C265.858398,127.432617 277.109894,118.064262 290.438477,116.993347 C322.543274,114.413818 354.683655,115.404877 386.813324,115.130096 C394.489471,115.064453 401.705994,116.824165 408.265320,120.840240 C420.633514,128.412872 422.526917,142.616318 418.216461,153.712448 C411.739807,170.384842 404.531006,186.774277 397.558136,203.252121 C386.530487,229.312042 375.498291,255.370331 364.355072,281.380920 C357.626984,297.085663 350.685486,312.698975 343.600830,328.661926 z"/>
|
||||
<path fill="#FCFDFD" opacity="1.000000" stroke="none" d=" M217.117798,326.634277 C221.556976,336.960388 227.093643,346.597900 229.804153,356.973724 C234.438812,374.715240 218.140427,393.432953 198.211578,388.023956 C189.952576,385.782349 183.895462,380.786255 180.372223,373.821899 C172.057144,357.385559 164.707733,340.457397 157.088318,323.673462 C151.241760,310.794708 145.483231,297.874054 139.856674,284.897827 C130.721710,263.830353 121.621635,242.746506 112.701210,221.587616 C102.994286,198.563171 93.242340,175.551285 84.013725,152.333710 C76.931572,134.516266 92.602669,116.441895 110.310730,117.172417 C120.190109,117.579979 128.268326,122.562485 132.329987,131.279694 C145.059433,158.599945 157.107895,186.239868 169.245682,213.832703 C180.019928,238.325790 190.660950,262.878815 201.164383,287.489166 C206.642761,300.325470 211.697678,313.342468 217.117798,326.634277 z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
@@ -1,372 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="500dp"
|
||||
android:height="500dp"
|
||||
android:viewportWidth="500"
|
||||
android:viewportHeight="500">
|
||||
<path
|
||||
android:pathData="M316,114L393,114L406,118L408,119L408,121L412,122L417,129L419,135L419,149L413,165L405,185L387,226L375,254L367,274L356,299L343,329L336,344L327,364L319,377L312,383L304,387L299,388L291,388L282,385L272,376L266,366L258,350L250,331L234,295L222,268L207,232L194,202L178,165L172,149L172,136L177,126L185,119L192,116L206,116L216,121L223,129L231,145L243,171L256,203L270,234L283,264L291,284L295,294L298,290L306,270L317,246L328,220L333,209L341,189L349,171L349,170L299,170L286,169L276,166L266,157L262,147L262,138L266,128L275,120L283,116L293,115Z"
|
||||
android:fillColor="#FCFCFC"/>
|
||||
<path
|
||||
android:pathData="M103,116L113,116L121,119L131,128L140,145L158,186L166,205L180,237L190,260L200,283L219,331L227,347L230,356L230,366L226,376L219,384L210,388L198,388L189,384L181,377L173,363L163,341L150,312L139,287L128,261L122,246L108,214L89,170L82,151L81,140L84,130L89,123L97,118Z"
|
||||
android:fillColor="#FCFCFC"/>
|
||||
<path
|
||||
android:pathData="M130,258L132,261L137,272L136,277L138,277L138,279L140,279L143,286L145,295L143,296L129,264L128,259Z"
|
||||
android:fillColor="#C8CACB"/>
|
||||
<path
|
||||
android:pathData="M94,179L97,179L96,183L99,184L98,188L100,188L100,190L102,190L107,203L106,207L103,203L94,182Z"
|
||||
android:fillColor="#C1C3CA"/>
|
||||
<path
|
||||
android:pathData="M339,194L341,195L339,204L332,219L330,223L327,224L329,219Z"
|
||||
android:fillColor="#DFE1E4"/>
|
||||
<path
|
||||
android:pathData="M400,187L403,187L402,192L394,210L393,210L393,204L398,190Z"
|
||||
android:fillColor="#B9BABE"/>
|
||||
<path
|
||||
android:pathData="M175,129L177,133L175,149L172,149L172,136Z"
|
||||
android:fillColor="#CBCBCE"/>
|
||||
<path
|
||||
android:pathData="M157,325L160,326L163,332L165,336L167,341L166,345L163,341L157,327Z"
|
||||
android:fillColor="#E7E7E9"/>
|
||||
<path
|
||||
android:pathData="M135,276L138,277L138,279L140,279L143,286L145,295L143,296L135,278Z"
|
||||
android:fillColor="#C0C3C4"/>
|
||||
<path
|
||||
android:pathData="M161,200L164,200L172,219L171,221L168,218L164,210Z"
|
||||
android:fillColor="#DADBDD"/>
|
||||
<path
|
||||
android:pathData="M330,349L333,349L330,358L325,368L324,364L329,351Z"
|
||||
android:fillColor="#A9AAAC"/>
|
||||
<path
|
||||
android:pathData="M265,130L267,131L266,135L265,139L263,139L264,145L266,146L266,152L264,153L262,147L262,138Z"
|
||||
android:fillColor="#8D9091"/>
|
||||
<path
|
||||
android:pathData="M339,330L341,330L340,335L335,346L333,343L338,331Z"
|
||||
android:fillColor="#E3E5E7"/>
|
||||
<path
|
||||
android:pathData="M120,240L122,240L123,243L125,244L129,254L130,259L127,258L120,241ZM127,254Z"
|
||||
android:fillColor="#E5E6E8"/>
|
||||
<path
|
||||
android:pathData="M169,353L173,354L173,356L175,357L177,363L175,367L169,355Z"
|
||||
android:fillColor="#C1C2C4"/>
|
||||
<path
|
||||
android:pathData="M190,190L195,194L197,200L197,206L194,202L190,193Z"
|
||||
android:fillColor="#C2C4C5"/>
|
||||
<path
|
||||
android:pathData="M260,353L263,356L266,362L268,362L270,367L268,370L260,355Z"
|
||||
android:fillColor="#929698"/>
|
||||
<path
|
||||
android:pathData="M400,187L403,187L402,192L401,194L399,194L399,196L397,196L396,198L397,192Z"
|
||||
android:fillColor="#CDCECF"/>
|
||||
<path
|
||||
android:pathData="M349,167L351,167L350,170L321,169L321,168Z"
|
||||
android:fillColor="#DFE2E3"/>
|
||||
<path
|
||||
android:pathData="M257,208L260,212L264,221L263,223L259,219L256,210Z"
|
||||
android:fillColor="#CED0D2"/>
|
||||
<path
|
||||
android:pathData="M339,194L341,195L339,204L335,208L334,206Z"
|
||||
android:fillColor="#B1B3B4"/>
|
||||
<path
|
||||
android:pathData="M89,168L94,171L96,177L94,177L93,179L89,170Z"
|
||||
android:fillColor="#B5B6B8"/>
|
||||
<path
|
||||
android:pathData="M270,237L273,241L275,248L271,247L269,241Z"
|
||||
android:fillColor="#BFC1C1"/>
|
||||
<path
|
||||
android:pathData="M326,224L327,228L326,235L321,239L324,229Z"
|
||||
android:fillColor="#C9CBCC"/>
|
||||
<path
|
||||
android:pathData="M94,179L97,179L96,183L99,184L98,188L100,188L101,192L99,193L94,182Z"
|
||||
android:fillColor="#A7A8A8"/>
|
||||
<path
|
||||
android:pathData="M272,122L274,123L271,129L267,132L266,128Z"
|
||||
android:fillColor="#CDCECF"/>
|
||||
<path
|
||||
android:pathData="M224,270L227,274L230,276L231,286L228,282L224,273Z"
|
||||
android:fillColor="#949D9F"/>
|
||||
<path
|
||||
android:pathData="M229,356L230,356L230,366L227,374L225,371L227,365L229,365Z"
|
||||
android:fillColor="#B7B9BA"/>
|
||||
<path
|
||||
android:pathData="M197,206L200,208L200,210L202,210L203,217L201,218L197,209Z"
|
||||
android:fillColor="#CCCDCF"/>
|
||||
<path
|
||||
android:pathData="M135,137L138,141L140,145L140,148L136,146L135,144Z"
|
||||
android:fillColor="#C4C4C5"/>
|
||||
<path
|
||||
android:pathData="M130,258L132,261L134,266L132,266L131,269L128,259Z"
|
||||
android:fillColor="#B7B8B9"/>
|
||||
<path
|
||||
android:pathData="M371,254L374,254L373,260L371,265L369,261Z"
|
||||
android:fillColor="#C1C2C2"/>
|
||||
<path
|
||||
android:pathData="M264,223L267,227L270,234L270,237L267,236L268,233L266,233Z"
|
||||
android:fillColor="#B2B3B5"/>
|
||||
<path
|
||||
android:pathData="M135,276L138,277L138,279L140,279L141,285L138,285Z"
|
||||
android:fillColor="#B1B2B3"/>
|
||||
<path
|
||||
android:pathData="M241,169L243,171L245,178L242,178L240,175ZM241,178Z"
|
||||
android:fillColor="#B4B6B7"/>
|
||||
<path
|
||||
android:pathData="M144,296L147,297L146,299L149,300L148,302L150,303L148,307L144,298Z"
|
||||
android:fillColor="#C4C6CB"/>
|
||||
<path
|
||||
android:pathData="M222,266L226,267L228,271L227,275L225,270L223,270Z"
|
||||
android:fillColor="#D3D5D5"/>
|
||||
<path
|
||||
android:pathData="M364,276L365,279L362,285L360,284L361,278Z"
|
||||
android:fillColor="#BABCBD"/>
|
||||
<path
|
||||
android:pathData="M172,221L175,225L177,232L174,231L172,227Z"
|
||||
android:fillColor="#BBBBBB"/>
|
||||
<path
|
||||
android:pathData="M207,308L210,308L213,318L210,316Z"
|
||||
android:fillColor="#D6DCDD"/>
|
||||
<path
|
||||
android:pathData="M302,280L303,280L303,288L299,289Z"
|
||||
android:fillColor="#BABBBC"/>
|
||||
<path
|
||||
android:pathData="M190,262L192,264L195,273L193,272L193,270L190,269L189,266Z"
|
||||
android:fillColor="#A3A4A8"/>
|
||||
<path
|
||||
android:pathData="M377,244L378,247L375,254L372,253L376,245Z"
|
||||
android:fillColor="#EFF5F8"/>
|
||||
<path
|
||||
android:pathData="M167,211L170,214L172,221L169,220L167,216Z"
|
||||
android:fillColor="#C5C7C7"/>
|
||||
<path
|
||||
android:pathData="M231,147L234,151L236,158L234,159L234,154L231,153Z"
|
||||
android:fillColor="#A7ABAD"/>
|
||||
<path
|
||||
android:pathData="M369,265L370,267L366,276L364,273L366,269L368,269L367,266Z"
|
||||
android:fillColor="#9E9EA2"/>
|
||||
<path
|
||||
android:pathData="M330,349L333,349L331,356L328,355Z"
|
||||
android:fillColor="#CACBCE"/>
|
||||
<path
|
||||
android:pathData="M256,343L260,344L261,348L259,348L258,350L256,346Z"
|
||||
android:fillColor="#C0C0C2"/>
|
||||
<path
|
||||
android:pathData="M202,218L204,218L207,224L206,227L204,225Z"
|
||||
android:fillColor="#CFD2D3"/>
|
||||
<path
|
||||
android:pathData="M402,179L406,180L405,185L403,187L403,185L401,184L403,183Z"
|
||||
android:fillColor="#B2B2B2"/>
|
||||
<path
|
||||
android:pathData="M204,300L207,300L209,307L206,307L206,303L204,303Z"
|
||||
android:fillColor="#B0B1B5"/>
|
||||
<path
|
||||
android:pathData="M195,274L198,278L199,283L196,282Z"
|
||||
android:fillColor="#999A9E"/>
|
||||
<path
|
||||
android:pathData="M206,227L209,228L211,233L208,234Z"
|
||||
android:fillColor="#D3D4E2"/>
|
||||
<path
|
||||
android:pathData="M392,205L393,205L393,213L391,215L390,209Z"
|
||||
android:fillColor="#DBDCDC"/>
|
||||
<path
|
||||
android:pathData="M259,350L263,351L264,356L262,356L259,352Z"
|
||||
android:fillColor="#DEDEE0"/>
|
||||
<path
|
||||
android:pathData="M285,166L289,167L295,167L291,169L284,168Z"
|
||||
android:fillColor="#F3F4F5"/>
|
||||
<path
|
||||
android:pathData="M173,150L176,151L178,156L176,156L175,158L173,153Z"
|
||||
android:fillColor="#A8A8A8"/>
|
||||
<path
|
||||
android:pathData="M264,145L266,146L266,152L264,153L263,146Z"
|
||||
android:fillColor="#BBBDBE"/>
|
||||
<path
|
||||
android:pathData="M175,129L177,133L175,137L173,136L174,131Z"
|
||||
android:fillColor="#B4B8BB"/>
|
||||
<path
|
||||
android:pathData="M34,498Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M323,493Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M495,485Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M43,480Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M388,476Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M103,471Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M36,456Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M276,453Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M27,445Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M206,442Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M221,441Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M386,431Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M37,422Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M175,420Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M199,417Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M72,412Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M307,392Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M409,387Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M170,386Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M90,382Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M164,374Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M131,361Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M478,351Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M441,346Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M448,343Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M27,339Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M412,334Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M63,324Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M222,322Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M129,322Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M87,309Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M461,247Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M466,239Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M23,229Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M462,224Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M15,206Z"
|
||||
android:fillColor="#000001"/>
|
||||
<path
|
||||
android:pathData="M185,203Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M34,181Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M6,180Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M462,140Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M163,131Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M483,119Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M174,118Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M405,110Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M267,90Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M242,83Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M55,76Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M309,71Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M318,66Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M457,61Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M373,47Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M350,44Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M371,41Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M329,41Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M85,37Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M428,36Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M238,32Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M21,30Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M358,29Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M146,26Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M175,22Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M389,19Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M392,9Z"
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
||||
@@ -45,7 +45,7 @@ import com.zaneschepke.wireguardautotunnel.desktop.ui.screens.support.license.Li
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.ui.screens.tunnels.TunnelsScreen
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.ui.screens.tunnels.tunnel.TunnelScreen
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.ui.state.AppUiState
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.ui.theme.AlertRed
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.ui.theme.ErrorRed
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.viewmodel.AppViewModel
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.viewmodel.TunnelViewModel
|
||||
import io.github.sudarshanmhasrup.localina.api.LocalinaApp
|
||||
@@ -158,7 +158,7 @@ fun App(uiState: AppUiState, viewModel: AppViewModel, toaster: ToasterState) {
|
||||
railState.targetValue == WideNavigationRailValue.Expanded,
|
||||
icon = {
|
||||
CustomTooltip(text = "Lockdown active") {
|
||||
Icon(Icons.Filled.Lock, "Lockdown active", tint = AlertRed)
|
||||
Icon(Icons.Filled.Lock, "Lockdown active", tint = ErrorRed)
|
||||
}
|
||||
},
|
||||
enabled = false,
|
||||
@@ -230,7 +230,7 @@ fun App(uiState: AppUiState, viewModel: AppViewModel, toaster: ToasterState) {
|
||||
entryProvider =
|
||||
entryProvider {
|
||||
currentTab.startRoute
|
||||
entry<Route.Tunnels> { TunnelsScreen() }
|
||||
entry<Route.Tunnels> { TunnelsScreen(viewModel) }
|
||||
entry<Route.Tunnel> {
|
||||
val viewModel: TunnelViewModel =
|
||||
koinViewModel(parameters = { parametersOf(it.id) })
|
||||
|
||||
+153
-108
@@ -1,22 +1,18 @@
|
||||
package com.zaneschepke.wireguardautotunnel.desktop
|
||||
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material.icons.filled.Error
|
||||
import androidx.compose.material.icons.filled.Info
|
||||
import androidx.compose.material.icons.filled.Warning
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
@@ -33,132 +29,181 @@ import com.dokar.sonner.rememberToasterState
|
||||
import com.kdroid.composetray.tray.api.ExperimentalTrayAppApi
|
||||
import com.kdroid.composetray.tray.api.Tray
|
||||
import com.kdroid.composetray.utils.SingleInstanceManager
|
||||
import com.kdroid.composetray.utils.isMenuBarInDarkMode
|
||||
import com.zaneschepke.wireguardautotunnel.client.di.databaseModule
|
||||
import com.zaneschepke.wireguardautotunnel.client.di.serviceModule
|
||||
import com.zaneschepke.wireguardautotunnel.composeapp.generated.resources.Res
|
||||
import com.zaneschepke.wireguardautotunnel.composeapp.generated.resources.app_name
|
||||
import com.zaneschepke.wireguardautotunnel.composeapp.generated.resources.wgtunnel
|
||||
import com.zaneschepke.wireguardautotunnel.composeapp.generated.resources.appicon
|
||||
import com.zaneschepke.wireguardautotunnel.core.helper.FilePathsHelper
|
||||
import com.zaneschepke.wireguardautotunnel.core.ipc.dto.TunnelState
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.di.viewModelModule
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.ui.common.bar.TitleBar
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.ui.screens.tunnels.components.asColor
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.ui.screens.tunnels.components.asTooltipMessage
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.ui.state.TrayBadgeState
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.ui.theme.ErrorRed
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.ui.theme.WGTunnelTheme
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.viewmodel.AppViewModel
|
||||
import java.nio.file.Paths
|
||||
import org.jetbrains.compose.resources.painterResource
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.jetbrains.compose.resources.vectorResource
|
||||
import org.koin.compose.KoinApplication
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
import org.koin.dsl.koinConfiguration
|
||||
import org.orbitmvi.orbit.compose.collectAsState
|
||||
|
||||
@OptIn(ExperimentalTrayAppApi::class)
|
||||
fun main() = application {
|
||||
var isWindowVisible by remember { mutableStateOf(true) }
|
||||
SingleInstanceManager.configuration =
|
||||
SingleInstanceManager.Configuration(
|
||||
lockFilesDir = Paths.get(FilePathsHelper.getDatabaseDir().path),
|
||||
lockIdentifier = "wg_tunnel",
|
||||
)
|
||||
val isSingleInstance =
|
||||
SingleInstanceManager.isSingleInstance(onRestoreRequest = { isWindowVisible = true })
|
||||
|
||||
if (!isSingleInstance) {
|
||||
exitApplication()
|
||||
return@application
|
||||
}
|
||||
|
||||
KoinApplication(
|
||||
configuration =
|
||||
koinConfiguration(
|
||||
declaration = { modules(databaseModule, serviceModule, viewModelModule) }
|
||||
fun main() {
|
||||
// TODO support GPU acceleration later, software for now for reliability
|
||||
System.setProperty("skiko.renderApi", "SOFTWARE_FASTEST")
|
||||
application {
|
||||
var isWindowVisible by remember { mutableStateOf(true) }
|
||||
SingleInstanceManager.configuration =
|
||||
SingleInstanceManager.Configuration(
|
||||
lockFilesDir = Paths.get(FilePathsHelper.getDatabaseDir().path),
|
||||
lockIdentifier = "wg_tunnel",
|
||||
)
|
||||
) {
|
||||
val appIcon = painterResource(Res.drawable.wgtunnel)
|
||||
val appName = stringResource(Res.string.app_name)
|
||||
val isSingleInstance =
|
||||
SingleInstanceManager.isSingleInstance(onRestoreRequest = { isWindowVisible = true })
|
||||
|
||||
val isMenuBarDark = isMenuBarInDarkMode()
|
||||
val windowState = rememberWindowState(size = DpSize(800.dp, 650.dp))
|
||||
val toaster = rememberToasterState()
|
||||
|
||||
Tray(
|
||||
iconContent = {
|
||||
Icon(
|
||||
imageVector = vectorResource(Res.drawable.wgtunnel),
|
||||
contentDescription = appName,
|
||||
tint = if (isMenuBarDark) Color.White else Color.Black,
|
||||
)
|
||||
},
|
||||
tooltip = appName,
|
||||
) {
|
||||
if (!isWindowVisible) {
|
||||
Item(label = "Open") {
|
||||
Logger.i { "Open menu item clicked" }
|
||||
isWindowVisible = true
|
||||
}
|
||||
}
|
||||
Item(label = "Exit") { exitApplication() }
|
||||
if (!isSingleInstance) {
|
||||
exitApplication()
|
||||
return@application
|
||||
}
|
||||
|
||||
Window(
|
||||
visible = isWindowVisible,
|
||||
onCloseRequest = {
|
||||
isWindowVisible = false
|
||||
Logger.i { "OS close requested - hiding to tray" }
|
||||
},
|
||||
title = stringResource(Res.string.app_name),
|
||||
icon = appIcon,
|
||||
state = windowState,
|
||||
undecorated = true,
|
||||
transparent = true,
|
||||
var trayBadgeState: TrayBadgeState? by remember { mutableStateOf(null) }
|
||||
|
||||
KoinApplication(
|
||||
configuration =
|
||||
koinConfiguration(
|
||||
declaration = { modules(databaseModule, serviceModule, viewModelModule) }
|
||||
)
|
||||
) {
|
||||
window.minimumSize = java.awt.Dimension(800, 650)
|
||||
val appIcon = painterResource(Res.drawable.appicon)
|
||||
val appName = stringResource(Res.string.app_name)
|
||||
|
||||
val viewModel: AppViewModel = koinViewModel()
|
||||
val uiState by viewModel.collectAsState()
|
||||
val windowState = rememberWindowState(size = DpSize(800.dp, 650.dp))
|
||||
val toaster = rememberToasterState()
|
||||
|
||||
WGTunnelTheme(uiState.theme) {
|
||||
Surface(
|
||||
modifier =
|
||||
Modifier.fillMaxSize().background(MaterialTheme.colorScheme.background),
|
||||
color = MaterialTheme.colorScheme.background,
|
||||
) {
|
||||
Column {
|
||||
TitleBar(onClose = { isWindowVisible = false })
|
||||
App(uiState, viewModel, toaster)
|
||||
Toaster(
|
||||
state = toaster,
|
||||
elevation = 0.dp,
|
||||
border = { BorderStroke(0.dp, Color.Transparent) },
|
||||
background = { SolidColor(MaterialTheme.colorScheme.inverseOnSurface) },
|
||||
iconSlot = {
|
||||
Icon(
|
||||
when (it.type) {
|
||||
ToastType.Normal,
|
||||
ToastType.Info -> Icons.Default.Info
|
||||
ToastType.Success -> Icons.Default.Check
|
||||
ToastType.Warning -> Icons.Default.Warning
|
||||
ToastType.Error -> Icons.Default.Error
|
||||
},
|
||||
null,
|
||||
modifier = Modifier.size(24.dp),
|
||||
tint = MaterialTheme.colorScheme.inverseSurface,
|
||||
)
|
||||
},
|
||||
messageSlot = {
|
||||
val message = it.message as? String ?: return@Toaster
|
||||
Text(
|
||||
message,
|
||||
color = MaterialTheme.colorScheme.inverseSurface,
|
||||
fontSize = 16.sp,
|
||||
modifier = Modifier.padding(start = 12.dp),
|
||||
)
|
||||
},
|
||||
contentColor = { MaterialTheme.colorScheme.inverseSurface },
|
||||
shape = { RoundedCornerShape(8.dp) },
|
||||
containerPadding = PaddingValues(48.dp),
|
||||
Tray(
|
||||
iconContent = {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
Image(
|
||||
painter = appIcon,
|
||||
contentDescription = appName,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
)
|
||||
trayBadgeState?.let {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Circle,
|
||||
contentDescription = it.description,
|
||||
modifier = Modifier.size(40.dp).align(Alignment.TopEnd),
|
||||
tint = it.iconColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip = appName,
|
||||
primaryAction = { isWindowVisible = true },
|
||||
) {
|
||||
if (!isWindowVisible) {
|
||||
Item(label = "Open") {
|
||||
Logger.i { "Open menu item clicked" }
|
||||
isWindowVisible = true
|
||||
}
|
||||
}
|
||||
Item(label = "Exit") { exitApplication() }
|
||||
}
|
||||
|
||||
Window(
|
||||
visible = isWindowVisible,
|
||||
onCloseRequest = {
|
||||
isWindowVisible = false
|
||||
Logger.i { "OS close requested - hiding to tray" }
|
||||
},
|
||||
title = stringResource(Res.string.app_name),
|
||||
icon = appIcon,
|
||||
state = windowState,
|
||||
undecorated = true,
|
||||
transparent = true,
|
||||
) {
|
||||
window.minimumSize = java.awt.Dimension(800, 650)
|
||||
|
||||
val viewModel: AppViewModel = koinViewModel()
|
||||
val uiState by viewModel.collectAsState()
|
||||
|
||||
LaunchedEffect(uiState.tunnelStatuses, uiState.lockdownActive) {
|
||||
if (uiState.tunnelStatuses.isEmpty() && !uiState.lockdownActive) {
|
||||
trayBadgeState = null
|
||||
return@LaunchedEffect
|
||||
}
|
||||
|
||||
val state: TunnelState? =
|
||||
(uiState.tunnelStatuses.firstOrNull {
|
||||
it.state == TunnelState.HANDSHAKE_FAILURE
|
||||
}
|
||||
?: uiState.tunnelStatuses.firstOrNull {
|
||||
it.state == TunnelState.RESOLVING_DNS ||
|
||||
it.state == TunnelState.STOPPING ||
|
||||
it.state == TunnelState.STARTING
|
||||
}
|
||||
?: uiState.tunnelStatuses.firstOrNull {
|
||||
it.state == TunnelState.HEALTHY
|
||||
})
|
||||
?.state
|
||||
|
||||
val (color, description) =
|
||||
when {
|
||||
uiState.lockdownActive && state == null -> ErrorRed to "Lockdown active"
|
||||
else -> state!!.asColor() to state.asTooltipMessage()
|
||||
}
|
||||
trayBadgeState = TrayBadgeState(color, description)
|
||||
}
|
||||
|
||||
WGTunnelTheme(uiState.theme) {
|
||||
Surface(
|
||||
modifier =
|
||||
Modifier.fillMaxSize().background(MaterialTheme.colorScheme.background),
|
||||
color = MaterialTheme.colorScheme.background,
|
||||
) {
|
||||
Column {
|
||||
TitleBar(onClose = { isWindowVisible = false })
|
||||
App(uiState, viewModel, toaster)
|
||||
Toaster(
|
||||
state = toaster,
|
||||
elevation = 0.dp,
|
||||
border = { BorderStroke(0.dp, Color.Transparent) },
|
||||
background = {
|
||||
SolidColor(MaterialTheme.colorScheme.inverseOnSurface)
|
||||
},
|
||||
iconSlot = {
|
||||
Icon(
|
||||
when (it.type) {
|
||||
ToastType.Normal,
|
||||
ToastType.Info -> Icons.Default.Info
|
||||
ToastType.Success -> Icons.Default.Check
|
||||
ToastType.Warning -> Icons.Default.Warning
|
||||
ToastType.Error -> Icons.Default.Error
|
||||
},
|
||||
null,
|
||||
modifier = Modifier.size(24.dp),
|
||||
tint = MaterialTheme.colorScheme.inverseSurface,
|
||||
)
|
||||
},
|
||||
messageSlot = {
|
||||
val message = it.message as? String ?: return@Toaster
|
||||
Text(
|
||||
message,
|
||||
color = MaterialTheme.colorScheme.inverseSurface,
|
||||
fontSize = 16.sp,
|
||||
modifier = Modifier.padding(start = 12.dp),
|
||||
)
|
||||
},
|
||||
contentColor = { MaterialTheme.colorScheme.inverseSurface },
|
||||
shape = { RoundedCornerShape(8.dp) },
|
||||
containerPadding = PaddingValues(48.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -18,12 +18,12 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.ui.theme.AlertRed
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.ui.theme.SilverTree
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.ui.theme.ErrorRed
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.ui.theme.HealthyGreen
|
||||
|
||||
@Composable
|
||||
fun PulsingStatusLed(isHealthy: Boolean, modifier: Modifier = Modifier) {
|
||||
val color = if (isHealthy) SilverTree else AlertRed
|
||||
val color = if (isHealthy) HealthyGreen else ErrorRed
|
||||
|
||||
val infiniteTransition = rememberInfiniteTransition(label = "PulseTransition")
|
||||
|
||||
|
||||
+8
-7
@@ -1,5 +1,6 @@
|
||||
package com.zaneschepke.wireguardautotunnel.desktop.ui.common.bar
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.window.WindowDraggableArea
|
||||
@@ -7,16 +8,16 @@ import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.CropSquare
|
||||
import androidx.compose.material.icons.filled.Remove
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.WindowScope
|
||||
import com.zaneschepke.wireguardautotunnel.composeapp.generated.resources.Res
|
||||
import com.zaneschepke.wireguardautotunnel.composeapp.generated.resources.select_window_2
|
||||
import com.zaneschepke.wireguardautotunnel.composeapp.generated.resources.wgtunnel
|
||||
import com.zaneschepke.wireguardautotunnel.composeapp.generated.resources.titleicon
|
||||
import java.awt.Frame
|
||||
import java.awt.event.WindowStateListener
|
||||
import org.jetbrains.compose.resources.painterResource
|
||||
@@ -41,16 +42,16 @@ fun WindowScope.TitleBar(onClose: () -> Unit) {
|
||||
Row(
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.height(40.dp)
|
||||
.height(24.dp)
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.padding(horizontal = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(Res.drawable.wgtunnel),
|
||||
Image(
|
||||
painterResource(Res.drawable.titleicon),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(20.dp),
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
modifier = Modifier.size(24.dp),
|
||||
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onBackground),
|
||||
)
|
||||
|
||||
Spacer(Modifier.weight(1f))
|
||||
|
||||
+4
-1
@@ -30,6 +30,7 @@ import com.zaneschepke.wireguardautotunnel.desktop.ui.common.tooltip.CustomToolt
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.ui.screens.tunnels.components.TunnelList
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.ui.sideeffects.AppSideEffect
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.util.FileUtils
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.viewmodel.AppViewModel
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.viewmodel.TunnelsViewModel
|
||||
import io.github.vinceglb.filekit.PlatformFile
|
||||
import io.github.vinceglb.filekit.dialogs.FileKitMode
|
||||
@@ -45,9 +46,10 @@ import org.orbitmvi.orbit.compose.collectSideEffect
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun TunnelsScreen(viewModel: TunnelsViewModel = koinViewModel()) {
|
||||
fun TunnelsScreen(appViewModel: AppViewModel, viewModel: TunnelsViewModel = koinViewModel()) {
|
||||
|
||||
val uiState by viewModel.collectAsState()
|
||||
val appUiState by appViewModel.collectAsState()
|
||||
|
||||
var pendingDeleteIntent by remember { mutableStateOf<DeleteIntent?>(null) }
|
||||
|
||||
@@ -171,6 +173,7 @@ fun TunnelsScreen(viewModel: TunnelsViewModel = koinViewModel()) {
|
||||
) {
|
||||
TunnelList(
|
||||
uiState = uiState,
|
||||
tunnelStatuses = appUiState.tunnelStatuses,
|
||||
startTunnel = viewModel::onStartTunnel,
|
||||
stopTunnel = viewModel::onStopTunnel,
|
||||
viewModel::onItemsReordered,
|
||||
|
||||
+8
-8
@@ -2,26 +2,26 @@ package com.zaneschepke.wireguardautotunnel.desktop.ui.screens.tunnels.component
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.zaneschepke.wireguardautotunnel.core.ipc.dto.TunnelState
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.ui.theme.AlertRed
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.ui.theme.SilverTree
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.ui.theme.Straw
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.ui.theme.ErrorRed
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.ui.theme.HealthyGreen
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.ui.theme.WarningAmber
|
||||
|
||||
fun TunnelState.asColor(): Color {
|
||||
return when (this) {
|
||||
TunnelState.DOWN,
|
||||
TunnelState.UNKNOWN -> Color.Gray
|
||||
TunnelState.HEALTHY -> SilverTree
|
||||
TunnelState.HANDSHAKE_FAILURE -> AlertRed
|
||||
TunnelState.HEALTHY -> HealthyGreen
|
||||
TunnelState.HANDSHAKE_FAILURE -> ErrorRed
|
||||
TunnelState.RESOLVING_DNS,
|
||||
TunnelState.STARTING,
|
||||
TunnelState.STOPPING -> Straw
|
||||
TunnelState.STOPPING -> WarningAmber
|
||||
}
|
||||
}
|
||||
|
||||
fun TunnelState.asTooltipMessage(): String? {
|
||||
fun TunnelState.asTooltipMessage(): String {
|
||||
return when (this) {
|
||||
TunnelState.DOWN,
|
||||
TunnelState.UNKNOWN -> null
|
||||
TunnelState.UNKNOWN -> "Unknown"
|
||||
TunnelState.STARTING -> "Starting"
|
||||
TunnelState.STOPPING -> "Stopping"
|
||||
TunnelState.HEALTHY -> "Healthy"
|
||||
|
||||
+5
-7
@@ -1,11 +1,7 @@
|
||||
package com.zaneschepke.wireguardautotunnel.desktop.ui.screens.tunnels.components
|
||||
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.foundation.ContextMenuArea
|
||||
import androidx.compose.foundation.ContextMenuItem
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -32,6 +28,7 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.zIndex
|
||||
import com.zaneschepke.wireguardautotunnel.client.domain.model.TunnelConfig
|
||||
import com.zaneschepke.wireguardautotunnel.core.ipc.dto.TunnelState
|
||||
import com.zaneschepke.wireguardautotunnel.core.ipc.dto.TunnelStatus
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.ui.common.LocalNavController
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.ui.common.button.SurfaceRow
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.ui.common.button.SwitchWithDivider
|
||||
@@ -51,6 +48,7 @@ import sh.calvin.reorderable.rememberReorderableLazyListState
|
||||
@Composable
|
||||
fun TunnelList(
|
||||
uiState: TunnelsUiState,
|
||||
tunnelStatuses: List<TunnelStatus>,
|
||||
startTunnel: (id: Long) -> Unit,
|
||||
stopTunnel: (id: Long) -> Unit,
|
||||
onReorder: (Int, Int) -> Unit,
|
||||
@@ -71,11 +69,11 @@ fun TunnelList(
|
||||
}
|
||||
|
||||
val tunnelIndicators by
|
||||
remember(uiState.tunnelStates, uiState.tunnels) {
|
||||
remember(tunnelStatuses, uiState.tunnels) {
|
||||
derivedStateOf {
|
||||
uiState.tunnels.associate { tunnel ->
|
||||
val state =
|
||||
uiState.tunnelStates.firstOrNull { it.id == tunnel.id }?.state
|
||||
tunnelStatuses.firstOrNull { it.id == tunnel.id }?.state
|
||||
?: TunnelState.UNKNOWN
|
||||
tunnel.id to (state.asColor() to state.asTooltipMessage())
|
||||
}
|
||||
|
||||
+2
@@ -1,6 +1,7 @@
|
||||
package com.zaneschepke.wireguardautotunnel.desktop.ui.state
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.client.data.model.Theme
|
||||
import com.zaneschepke.wireguardautotunnel.core.ipc.dto.TunnelStatus
|
||||
|
||||
data class AppUiState(
|
||||
val isLoaded: Boolean = false,
|
||||
@@ -9,6 +10,7 @@ data class AppUiState(
|
||||
val locale: String = DEFAULT_LOCALE,
|
||||
val alreadyDonated: Boolean = false,
|
||||
val lockdownActive: Boolean = false,
|
||||
val tunnelStatuses: List<TunnelStatus> = emptyList(),
|
||||
) {
|
||||
companion object {
|
||||
const val DEFAULT_LOCALE = "en-US"
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package com.zaneschepke.wireguardautotunnel.desktop.ui.state
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
data class TrayBadgeState(val iconColor: Color, val description: String)
|
||||
-2
@@ -1,11 +1,9 @@
|
||||
package com.zaneschepke.wireguardautotunnel.desktop.ui.state
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.client.domain.model.TunnelConfig
|
||||
import com.zaneschepke.wireguardautotunnel.core.ipc.dto.TunnelStatus
|
||||
|
||||
data class TunnelsUiState(
|
||||
val tunnels: List<TunnelConfig> = emptyList(),
|
||||
val tunnelStates: List<TunnelStatus> = emptyList(),
|
||||
val selectedTunnels: List<TunnelConfig> = emptyList(),
|
||||
val isSelectionMode: Boolean = false,
|
||||
val isLoaded: Boolean = false,
|
||||
|
||||
+3
-3
@@ -14,7 +14,7 @@ val BalticSea = Color(0xFF1C1B1F)
|
||||
val ElectricTeal = Color(0xFF4DD0E1)
|
||||
|
||||
// Status colors
|
||||
val SilverTree = Color(0xFF6DB58B)
|
||||
val AlertRed = Color(0xFFCF6679)
|
||||
val Straw = Color(0xFFD4C483)
|
||||
val HealthyGreen = Color(0xFF26A69A)
|
||||
val ErrorRed = Color(0xFFE53935)
|
||||
val WarningAmber = Color(0xFFFFB300)
|
||||
val Disabled = CoolGray.copy(alpha = 0.4f)
|
||||
|
||||
+11
-3
@@ -8,6 +8,8 @@ import com.zaneschepke.wireguardautotunnel.client.service.DaemonService
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.ui.sideeffects.AppSideEffect
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.ui.state.AppUiState
|
||||
import io.github.sudarshanmhasrup.localina.api.LocaleUpdater
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.map
|
||||
import org.orbitmvi.orbit.ContainerHost
|
||||
import org.orbitmvi.orbit.viewmodel.container
|
||||
|
||||
@@ -39,9 +41,15 @@ class AppViewModel(
|
||||
}
|
||||
intent { daemonService.alive.collect { reduce { state.copy(daemonConnected = it) } } }
|
||||
intent {
|
||||
backendService.statusFlow().collect {
|
||||
reduce { state.copy(lockdownActive = it.killSwitchEnabled) }
|
||||
}
|
||||
backendService
|
||||
.statusFlow()
|
||||
.map { it.killSwitchEnabled to it.activeTunnels }
|
||||
.distinctUntilChanged()
|
||||
.collect { (lockdown, tunnelStates) ->
|
||||
reduce {
|
||||
state.copy(tunnelStatuses = tunnelStates, lockdownActive = lockdown)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-10
@@ -5,7 +5,6 @@ import com.dokar.sonner.ToastType
|
||||
import com.zaneschepke.wireguardautotunnel.client.domain.error.ClientException
|
||||
import com.zaneschepke.wireguardautotunnel.client.domain.model.TunnelConfig
|
||||
import com.zaneschepke.wireguardautotunnel.client.domain.repository.TunnelRepository
|
||||
import com.zaneschepke.wireguardautotunnel.client.service.BackendService
|
||||
import com.zaneschepke.wireguardautotunnel.client.service.TunnelImportService
|
||||
import com.zaneschepke.wireguardautotunnel.client.service.TunnelService
|
||||
import com.zaneschepke.wireguardautotunnel.desktop.ui.screens.tunnels.DeleteIntent
|
||||
@@ -19,14 +18,12 @@ import io.github.vinceglb.filekit.dialogs.openFileSaver
|
||||
import io.github.vinceglb.filekit.name
|
||||
import io.github.vinceglb.filekit.write
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.map
|
||||
import org.orbitmvi.orbit.ContainerHost
|
||||
import org.orbitmvi.orbit.viewmodel.container
|
||||
|
||||
class TunnelsViewModel(
|
||||
private val tunnelRepository: TunnelRepository,
|
||||
private val backendService: BackendService,
|
||||
private val tunnelService: TunnelService,
|
||||
private val tunnelImportService: TunnelImportService,
|
||||
) : ContainerHost<TunnelsUiState, AppSideEffect>, ViewModel() {
|
||||
@@ -41,13 +38,6 @@ class TunnelsViewModel(
|
||||
reduce { state.copy(tunnels = tunnels, isLoaded = true) }
|
||||
}
|
||||
}
|
||||
intent {
|
||||
backendService
|
||||
.statusFlow()
|
||||
.map { it.activeTunnels }
|
||||
.distinctUntilChanged()
|
||||
.collect { reduce { state.copy(tunnelStates = it) } }
|
||||
}
|
||||
}
|
||||
|
||||
fun onItemsReordered(fromIndex: Int, toIndex: Int) = intent {
|
||||
|
||||
+3
-1
@@ -1,9 +1,11 @@
|
||||
include required("conveyor.conf")
|
||||
|
||||
# Replace IP with machine IP for testing on private network
|
||||
app {
|
||||
site {
|
||||
copy-to = "./local-site"
|
||||
base-url = "http://localhost"
|
||||
base-url = "localhost:5500"
|
||||
consistency-checks = "warn"
|
||||
}
|
||||
ignore-connection-issues-for-hosts += "localhost:5500"
|
||||
}
|
||||
Reference in New Issue
Block a user