feat(iOS): Add Resource details view (#5129)

refs #3514 

- Show more Resource details when tapping a Resource
- Update logo text to be a stacked variant and SVG so it scales better,
also apply to all scale sizes
- Refactor SessionView so that Resources occupy more space, and move
auth controls to contextual menu
- Add `iOSNavigationView` that wraps child views to keep the navbar
controls consistent

<p align="center">
<img
src="https://github.com/firezone/firezone/assets/167144/6b337227-2b32-4864-b5b0-47a541b94476"
width="300">
</p>

--- 

<p align="center">
<img
src="https://github.com/firezone/firezone/assets/167144/5e129c38-37b5-4487-9b34-36b303434fac"
width="300">
</p>

---

<p align="center">
<img
src="https://github.com/firezone/firezone/assets/167144/edc56e34-a660-4256-a719-6886ff63c1a1"
width="300">
</p>

---


![IMG_0069](https://github.com/firezone/firezone/assets/167144/48c6bd87-7297-4fb4-81fa-3fa96110a17e)
This commit is contained in:
Jamil
2024-05-28 10:26:44 -07:00
committed by GitHub
parent 6408131809
commit e2f1617558
19 changed files with 500 additions and 239 deletions

View File

@@ -1,7 +1,7 @@
{
"images" : [
{
"filename" : "logo-text-any.svg",
"filename" : "welcomeview-light.svg",
"idiom" : "universal",
"scale" : "1x"
},
@@ -12,7 +12,7 @@
"value" : "light"
}
],
"filename" : "logo-text-light.svg",
"filename" : "welcomeview-light 3.svg",
"idiom" : "universal",
"scale" : "1x"
},
@@ -23,11 +23,12 @@
"value" : "dark"
}
],
"filename" : "logo-text-dark.svg",
"filename" : "welcomeview-dark.svg",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "welcomeview-light 1.svg",
"idiom" : "universal",
"scale" : "2x"
},
@@ -38,6 +39,7 @@
"value" : "light"
}
],
"filename" : "welcomeview-light 4.svg",
"idiom" : "universal",
"scale" : "2x"
},
@@ -48,10 +50,12 @@
"value" : "dark"
}
],
"filename" : "welcomeview-dark 1.svg",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "welcomeview-light 2.svg",
"idiom" : "universal",
"scale" : "3x"
},
@@ -62,6 +66,7 @@
"value" : "light"
}
],
"filename" : "welcomeview-light 5.svg",
"idiom" : "universal",
"scale" : "3x"
},
@@ -72,6 +77,7 @@
"value" : "dark"
}
],
"filename" : "welcomeview-dark 2.svg",
"idiom" : "universal",
"scale" : "3x"
}

View File

@@ -1,13 +0,0 @@
<svg width="325" height="93" viewBox="0 0 325 93" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_893_528)">
<path d="M114.312 71V29.144H139.592V35.416H121.736V47.256H136.968V53.528H121.736V71H114.312ZM143.097 71V39.576H150.457V71H143.097ZM146.745 34.072C145.465 34.072 144.398 33.688 143.545 32.92C142.734 32.152 142.329 31.128 142.329 29.848C142.329 28.568 142.734 27.5653 143.545 26.84C144.398 26.072 145.465 25.688 146.745 25.688C148.068 25.688 149.134 26.072 149.945 26.84C150.756 27.5653 151.161 28.568 151.161 29.848C151.161 31.128 150.756 32.152 149.945 32.92C149.134 33.688 148.068 34.072 146.745 34.072ZM156.647 71V39.576H162.727L163.239 45.144H163.495C164.604 43.096 165.927 41.5387 167.463 40.472C169.042 39.3627 170.684 38.808 172.391 38.808C173.202 38.808 173.884 38.8507 174.439 38.936C174.994 39.0213 175.506 39.192 175.975 39.448L174.503 45.72C173.991 45.592 173.522 45.5067 173.095 45.464C172.668 45.3787 172.114 45.336 171.431 45.336C170.194 45.336 168.892 45.8267 167.527 46.808C166.162 47.7467 164.988 49.4107 164.007 51.8V71H156.647ZM190.201 71.768C187.257 71.768 184.59 71.128 182.201 69.848C179.854 68.5253 177.998 66.648 176.633 64.216C175.268 61.7413 174.585 58.7547 174.585 55.256C174.585 51.8427 175.289 48.92 176.697 46.488C178.105 44.0133 179.918 42.1147 182.137 40.792C184.356 39.4693 186.702 38.808 189.177 38.808C192.036 38.808 194.425 39.448 196.345 40.728C198.265 42.008 199.694 43.7787 200.633 46.04C201.614 48.2587 202.105 50.8187 202.105 53.72C202.105 54.4027 202.062 55.064 201.977 55.704C201.934 56.344 201.849 56.9627 201.721 57.56H180.089V51.928H195.577C195.577 49.6667 195.065 47.896 194.041 46.616C193.017 45.2933 191.438 44.632 189.305 44.632C188.11 44.632 186.937 44.952 185.785 45.592C184.676 46.232 183.737 47.32 182.969 48.856C182.244 50.392 181.881 52.5253 181.881 55.256C181.881 57.7307 182.308 59.7573 183.161 61.336C184.014 62.872 185.145 64.024 186.553 64.792C187.961 65.5173 189.476 65.88 191.097 65.88C192.462 65.88 193.742 65.688 194.937 65.304C196.174 64.92 197.326 64.3867 198.393 63.704L200.953 68.44C199.502 69.464 197.838 70.2747 195.961 70.872C194.126 71.4693 192.206 71.768 190.201 71.768ZM203.188 71V67.032L218.036 45.4H204.852V39.576H227.252V43.48L212.468 65.112H227.764V71H203.188ZM243.099 71.768C240.496 71.768 238.064 71.128 235.803 69.848C233.541 68.5253 231.707 66.648 230.299 64.216C228.891 61.7413 228.187 58.776 228.187 55.32C228.187 51.8213 228.891 48.856 230.299 46.424C231.707 43.9493 233.541 42.072 235.803 40.792C238.064 39.4693 240.496 38.808 243.099 38.808C245.744 38.808 248.197 39.4693 250.459 40.792C252.72 42.072 254.555 43.9493 255.963 46.424C257.371 48.856 258.075 51.8213 258.075 55.32C258.075 58.776 257.371 61.7413 255.963 64.216C254.555 66.648 252.72 68.5253 250.459 69.848C248.197 71.128 245.744 71.768 243.099 71.768ZM243.099 65.752C244.635 65.752 245.957 65.3253 247.067 64.472C248.176 63.6187 249.029 62.4027 249.627 60.824C250.224 59.2453 250.523 57.4107 250.523 55.32C250.523 53.2293 250.224 51.3947 249.627 49.816C249.029 48.2373 248.176 47.0213 247.067 46.168C245.957 45.272 244.635 44.824 243.099 44.824C241.563 44.824 240.24 45.272 239.131 46.168C238.064 47.0213 237.232 48.2373 236.635 49.816C236.037 51.3947 235.739 53.2293 235.739 55.32C235.739 57.4107 236.037 59.2453 236.635 60.824C237.232 62.4027 238.064 63.6187 239.131 64.472C240.24 65.3253 241.563 65.752 243.099 65.752ZM262.16 71V39.576H268.24L268.752 43.8H269.008C270.416 42.4347 271.952 41.2613 273.616 40.28C275.28 39.2987 277.178 38.808 279.312 38.808C282.682 38.808 285.136 39.896 286.672 42.072C288.208 44.248 288.976 47.32 288.976 51.288V71H281.616V52.248C281.616 49.6453 281.232 47.8107 280.464 46.744C279.696 45.6773 278.437 45.144 276.688 45.144C275.322 45.144 274.106 45.4853 273.04 46.168C272.016 46.808 270.842 47.768 269.52 49.048V71H262.16ZM308.276 71.768C305.332 71.768 302.665 71.128 300.276 69.848C297.929 68.5253 296.073 66.648 294.708 64.216C293.343 61.7413 292.66 58.7547 292.66 55.256C292.66 51.8427 293.364 48.92 294.772 46.488C296.18 44.0133 297.993 42.1147 300.212 40.792C302.431 39.4693 304.777 38.808 307.252 38.808C310.111 38.808 312.5 39.448 314.42 40.728C316.34 42.008 317.769 43.7787 318.708 46.04C319.689 48.2587 320.18 50.8187 320.18 53.72C320.18 54.4027 320.137 55.064 320.052 55.704C320.009 56.344 319.924 56.9627 319.796 57.56H298.164V51.928H313.652C313.652 49.6667 313.14 47.896 312.116 46.616C311.092 45.2933 309.513 44.632 307.38 44.632C306.185 44.632 305.012 44.952 303.86 45.592C302.751 46.232 301.812 47.32 301.044 48.856C300.319 50.392 299.956 52.5253 299.956 55.256C299.956 57.7307 300.383 59.7573 301.236 61.336C302.089 62.872 303.22 64.024 304.628 64.792C306.036 65.5173 307.551 65.88 309.172 65.88C310.537 65.88 311.817 65.688 313.012 65.304C314.249 64.92 315.401 64.3867 316.468 63.704L319.028 68.44C317.577 69.464 315.913 70.2747 314.036 70.872C312.201 71.4693 310.281 71.768 308.276 71.768Z" fill="#1B140E"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M66.2375 4C86.1001 20.5434 65.5376 57.2083 72.7938 72.0126C57.8826 50.6161 78.1985 32.2283 66.2375 4Z" fill="#C25700"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M77.2523 35.8429C90.235 44.2787 74.6509 64.0444 82.9475 69.1039C91.35 74.2282 90.399 52.9323 102 58.3953C89.921 54.2857 94.932 79.64 79.9646 76.2658C62.8363 72.4045 84.1376 45.1601 77.2523 35.8429Z" fill="#8D1E00"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M2 64.7276C26.7271 45.0266 60.4083 76.6411 76.3024 78.6166C96.743 81.1568 91.298 57.5234 101.529 58.4039C92.101 59.5408 97.76 84.2573 76.6835 85.0211C53.7302 85.853 30.8299 50.8576 2 64.7276Z" fill="#1B140E"/>
</g>
<defs>
<clipPath id="clip0_893_528">
<rect width="325" height="93" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 5.6 KiB

View File

@@ -1,13 +0,0 @@
<svg width="349" height="110" viewBox="0 0 349 110" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_415_820)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M78.2375 14C98.1001 30.5434 77.5376 67.2083 84.7938 82.0126C69.8826 60.6161 90.1985 42.2283 78.2375 14Z" fill="#C25700"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M89.2523 45.843C102.235 54.2788 86.6509 74.0445 94.9475 79.104C103.35 84.2283 102.399 62.9324 114 68.3954C101.921 64.2858 106.932 89.6401 91.9646 86.2659C74.8363 82.4046 96.1376 55.1602 89.2523 45.843Z" fill="#C25700"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M14 74.7275C38.7271 55.0265 72.4083 86.641 88.3024 88.6165C108.743 91.1567 103.298 67.5233 113.529 68.4038C104.101 69.5407 109.76 94.2572 88.6835 95.021C65.7302 95.8529 42.8299 60.8575 14 74.7275Z" fill="#C25700"/>
<path d="M126.312 81V39.144H151.592V45.416H133.736V57.256H148.968V63.528H133.736V81H126.312ZM155.097 81V49.576H162.457V81H155.097ZM158.745 44.072C157.465 44.072 156.398 43.688 155.545 42.92C154.734 42.152 154.329 41.128 154.329 39.848C154.329 38.568 154.734 37.5653 155.545 36.84C156.398 36.072 157.465 35.688 158.745 35.688C160.068 35.688 161.134 36.072 161.945 36.84C162.756 37.5653 163.161 38.568 163.161 39.848C163.161 41.128 162.756 42.152 161.945 42.92C161.134 43.688 160.068 44.072 158.745 44.072ZM168.647 81V49.576H174.727L175.239 55.144H175.495C176.604 53.096 177.927 51.5387 179.463 50.472C181.042 49.3627 182.684 48.808 184.391 48.808C185.202 48.808 185.884 48.8507 186.439 48.936C186.994 49.0213 187.506 49.192 187.975 49.448L186.503 55.72C185.991 55.592 185.522 55.5067 185.095 55.464C184.668 55.3787 184.114 55.336 183.431 55.336C182.194 55.336 180.892 55.8267 179.527 56.808C178.162 57.7467 176.988 59.4107 176.007 61.8V81H168.647ZM202.201 81.768C199.257 81.768 196.59 81.128 194.201 79.848C191.854 78.5253 189.998 76.648 188.633 74.216C187.268 71.7413 186.585 68.7547 186.585 65.256C186.585 61.8427 187.289 58.92 188.697 56.488C190.105 54.0133 191.918 52.1147 194.137 50.792C196.356 49.4693 198.702 48.808 201.177 48.808C204.036 48.808 206.425 49.448 208.345 50.728C210.265 52.008 211.694 53.7787 212.633 56.04C213.614 58.2587 214.105 60.8187 214.105 63.72C214.105 64.4027 214.062 65.064 213.977 65.704C213.934 66.344 213.849 66.9627 213.721 67.56H192.089V61.928H207.577C207.577 59.6667 207.065 57.896 206.041 56.616C205.017 55.2933 203.438 54.632 201.305 54.632C200.11 54.632 198.937 54.952 197.785 55.592C196.676 56.232 195.737 57.32 194.969 58.856C194.244 60.392 193.881 62.5253 193.881 65.256C193.881 67.7307 194.308 69.7573 195.161 71.336C196.014 72.872 197.145 74.024 198.553 74.792C199.961 75.5173 201.476 75.88 203.097 75.88C204.462 75.88 205.742 75.688 206.937 75.304C208.174 74.92 209.326 74.3867 210.393 73.704L212.953 78.44C211.502 79.464 209.838 80.2747 207.961 80.872C206.126 81.4693 204.206 81.768 202.201 81.768ZM215.188 81V77.032L230.036 55.4H216.852V49.576H239.252V53.48L224.468 75.112H239.764V81H215.188ZM255.099 81.768C252.496 81.768 250.064 81.128 247.803 79.848C245.541 78.5253 243.707 76.648 242.299 74.216C240.891 71.7413 240.187 68.776 240.187 65.32C240.187 61.8213 240.891 58.856 242.299 56.424C243.707 53.9493 245.541 52.072 247.803 50.792C250.064 49.4693 252.496 48.808 255.099 48.808C257.744 48.808 260.197 49.4693 262.459 50.792C264.72 52.072 266.555 53.9493 267.963 56.424C269.371 58.856 270.075 61.8213 270.075 65.32C270.075 68.776 269.371 71.7413 267.963 74.216C266.555 76.648 264.72 78.5253 262.459 79.848C260.197 81.128 257.744 81.768 255.099 81.768ZM255.099 75.752C256.635 75.752 257.957 75.3253 259.067 74.472C260.176 73.6187 261.029 72.4027 261.627 70.824C262.224 69.2453 262.523 67.4107 262.523 65.32C262.523 63.2293 262.224 61.3947 261.627 59.816C261.029 58.2373 260.176 57.0213 259.067 56.168C257.957 55.272 256.635 54.824 255.099 54.824C253.563 54.824 252.24 55.272 251.131 56.168C250.064 57.0213 249.232 58.2373 248.635 59.816C248.037 61.3947 247.739 63.2293 247.739 65.32C247.739 67.4107 248.037 69.2453 248.635 70.824C249.232 72.4027 250.064 73.6187 251.131 74.472C252.24 75.3253 253.563 75.752 255.099 75.752ZM274.16 81V49.576H280.24L280.752 53.8H281.008C282.416 52.4347 283.952 51.2613 285.616 50.28C287.28 49.2987 289.178 48.808 291.312 48.808C294.682 48.808 297.136 49.896 298.672 52.072C300.208 54.248 300.976 57.32 300.976 61.288V81H293.616V62.248C293.616 59.6453 293.232 57.8107 292.464 56.744C291.696 55.6773 290.437 55.144 288.688 55.144C287.322 55.144 286.106 55.4853 285.04 56.168C284.016 56.808 282.842 57.768 281.52 59.048V81H274.16ZM320.276 81.768C317.332 81.768 314.665 81.128 312.276 79.848C309.929 78.5253 308.073 76.648 306.708 74.216C305.343 71.7413 304.66 68.7547 304.66 65.256C304.66 61.8427 305.364 58.92 306.772 56.488C308.18 54.0133 309.993 52.1147 312.212 50.792C314.431 49.4693 316.777 48.808 319.252 48.808C322.111 48.808 324.5 49.448 326.42 50.728C328.34 52.008 329.769 53.7787 330.708 56.04C331.689 58.2587 332.18 60.8187 332.18 63.72C332.18 64.4027 332.137 65.064 332.052 65.704C332.009 66.344 331.924 66.9627 331.796 67.56H310.164V61.928H325.652C325.652 59.6667 325.14 57.896 324.116 56.616C323.092 55.2933 321.513 54.632 319.38 54.632C318.185 54.632 317.012 54.952 315.86 55.592C314.751 56.232 313.812 57.32 313.044 58.856C312.319 60.392 311.956 62.5253 311.956 65.256C311.956 67.7307 312.383 69.7573 313.236 71.336C314.089 72.872 315.22 74.024 316.628 74.792C318.036 75.5173 319.551 75.88 321.172 75.88C322.537 75.88 323.817 75.688 325.012 75.304C326.249 74.92 327.401 74.3867 328.468 73.704L331.028 78.44C329.577 79.464 327.913 80.2747 326.036 80.872C324.201 81.4693 322.281 81.768 320.276 81.768Z" fill="#FCFCFC"/>
</g>
<defs>
<clipPath id="clip0_415_820">
<rect width="349" height="110" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 5.6 KiB

View File

@@ -1,13 +0,0 @@
<svg width="325" height="93" viewBox="0 0 325 93" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_893_528)">
<path d="M114.312 71V29.144H139.592V35.416H121.736V47.256H136.968V53.528H121.736V71H114.312ZM143.097 71V39.576H150.457V71H143.097ZM146.745 34.072C145.465 34.072 144.398 33.688 143.545 32.92C142.734 32.152 142.329 31.128 142.329 29.848C142.329 28.568 142.734 27.5653 143.545 26.84C144.398 26.072 145.465 25.688 146.745 25.688C148.068 25.688 149.134 26.072 149.945 26.84C150.756 27.5653 151.161 28.568 151.161 29.848C151.161 31.128 150.756 32.152 149.945 32.92C149.134 33.688 148.068 34.072 146.745 34.072ZM156.647 71V39.576H162.727L163.239 45.144H163.495C164.604 43.096 165.927 41.5387 167.463 40.472C169.042 39.3627 170.684 38.808 172.391 38.808C173.202 38.808 173.884 38.8507 174.439 38.936C174.994 39.0213 175.506 39.192 175.975 39.448L174.503 45.72C173.991 45.592 173.522 45.5067 173.095 45.464C172.668 45.3787 172.114 45.336 171.431 45.336C170.194 45.336 168.892 45.8267 167.527 46.808C166.162 47.7467 164.988 49.4107 164.007 51.8V71H156.647ZM190.201 71.768C187.257 71.768 184.59 71.128 182.201 69.848C179.854 68.5253 177.998 66.648 176.633 64.216C175.268 61.7413 174.585 58.7547 174.585 55.256C174.585 51.8427 175.289 48.92 176.697 46.488C178.105 44.0133 179.918 42.1147 182.137 40.792C184.356 39.4693 186.702 38.808 189.177 38.808C192.036 38.808 194.425 39.448 196.345 40.728C198.265 42.008 199.694 43.7787 200.633 46.04C201.614 48.2587 202.105 50.8187 202.105 53.72C202.105 54.4027 202.062 55.064 201.977 55.704C201.934 56.344 201.849 56.9627 201.721 57.56H180.089V51.928H195.577C195.577 49.6667 195.065 47.896 194.041 46.616C193.017 45.2933 191.438 44.632 189.305 44.632C188.11 44.632 186.937 44.952 185.785 45.592C184.676 46.232 183.737 47.32 182.969 48.856C182.244 50.392 181.881 52.5253 181.881 55.256C181.881 57.7307 182.308 59.7573 183.161 61.336C184.014 62.872 185.145 64.024 186.553 64.792C187.961 65.5173 189.476 65.88 191.097 65.88C192.462 65.88 193.742 65.688 194.937 65.304C196.174 64.92 197.326 64.3867 198.393 63.704L200.953 68.44C199.502 69.464 197.838 70.2747 195.961 70.872C194.126 71.4693 192.206 71.768 190.201 71.768ZM203.188 71V67.032L218.036 45.4H204.852V39.576H227.252V43.48L212.468 65.112H227.764V71H203.188ZM243.099 71.768C240.496 71.768 238.064 71.128 235.803 69.848C233.541 68.5253 231.707 66.648 230.299 64.216C228.891 61.7413 228.187 58.776 228.187 55.32C228.187 51.8213 228.891 48.856 230.299 46.424C231.707 43.9493 233.541 42.072 235.803 40.792C238.064 39.4693 240.496 38.808 243.099 38.808C245.744 38.808 248.197 39.4693 250.459 40.792C252.72 42.072 254.555 43.9493 255.963 46.424C257.371 48.856 258.075 51.8213 258.075 55.32C258.075 58.776 257.371 61.7413 255.963 64.216C254.555 66.648 252.72 68.5253 250.459 69.848C248.197 71.128 245.744 71.768 243.099 71.768ZM243.099 65.752C244.635 65.752 245.957 65.3253 247.067 64.472C248.176 63.6187 249.029 62.4027 249.627 60.824C250.224 59.2453 250.523 57.4107 250.523 55.32C250.523 53.2293 250.224 51.3947 249.627 49.816C249.029 48.2373 248.176 47.0213 247.067 46.168C245.957 45.272 244.635 44.824 243.099 44.824C241.563 44.824 240.24 45.272 239.131 46.168C238.064 47.0213 237.232 48.2373 236.635 49.816C236.037 51.3947 235.739 53.2293 235.739 55.32C235.739 57.4107 236.037 59.2453 236.635 60.824C237.232 62.4027 238.064 63.6187 239.131 64.472C240.24 65.3253 241.563 65.752 243.099 65.752ZM262.16 71V39.576H268.24L268.752 43.8H269.008C270.416 42.4347 271.952 41.2613 273.616 40.28C275.28 39.2987 277.178 38.808 279.312 38.808C282.682 38.808 285.136 39.896 286.672 42.072C288.208 44.248 288.976 47.32 288.976 51.288V71H281.616V52.248C281.616 49.6453 281.232 47.8107 280.464 46.744C279.696 45.6773 278.437 45.144 276.688 45.144C275.322 45.144 274.106 45.4853 273.04 46.168C272.016 46.808 270.842 47.768 269.52 49.048V71H262.16ZM308.276 71.768C305.332 71.768 302.665 71.128 300.276 69.848C297.929 68.5253 296.073 66.648 294.708 64.216C293.343 61.7413 292.66 58.7547 292.66 55.256C292.66 51.8427 293.364 48.92 294.772 46.488C296.18 44.0133 297.993 42.1147 300.212 40.792C302.431 39.4693 304.777 38.808 307.252 38.808C310.111 38.808 312.5 39.448 314.42 40.728C316.34 42.008 317.769 43.7787 318.708 46.04C319.689 48.2587 320.18 50.8187 320.18 53.72C320.18 54.4027 320.137 55.064 320.052 55.704C320.009 56.344 319.924 56.9627 319.796 57.56H298.164V51.928H313.652C313.652 49.6667 313.14 47.896 312.116 46.616C311.092 45.2933 309.513 44.632 307.38 44.632C306.185 44.632 305.012 44.952 303.86 45.592C302.751 46.232 301.812 47.32 301.044 48.856C300.319 50.392 299.956 52.5253 299.956 55.256C299.956 57.7307 300.383 59.7573 301.236 61.336C302.089 62.872 303.22 64.024 304.628 64.792C306.036 65.5173 307.551 65.88 309.172 65.88C310.537 65.88 311.817 65.688 313.012 65.304C314.249 64.92 315.401 64.3867 316.468 63.704L319.028 68.44C317.577 69.464 315.913 70.2747 314.036 70.872C312.201 71.4693 310.281 71.768 308.276 71.768Z" fill="#1B140E"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M66.2375 4C86.1001 20.5434 65.5376 57.2083 72.7938 72.0126C57.8826 50.6161 78.1985 32.2283 66.2375 4Z" fill="#C25700"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M77.2523 35.8429C90.235 44.2787 74.6509 64.0444 82.9475 69.1039C91.35 74.2282 90.399 52.9323 102 58.3953C89.921 54.2857 94.932 79.64 79.9646 76.2658C62.8363 72.4045 84.1376 45.1601 77.2523 35.8429Z" fill="#8D1E00"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M2 64.7276C26.7271 45.0266 60.4083 76.6411 76.3024 78.6166C96.743 81.1568 91.298 57.5234 101.529 58.4039C92.101 59.5408 97.76 84.2573 76.6835 85.0211C53.7302 85.853 30.8299 50.8576 2 64.7276Z" fill="#1B140E"/>
</g>
<defs>
<clipPath id="clip0_893_528">
<rect width="325" height="93" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 5.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@@ -38,13 +38,13 @@ public class AppViewModel: ObservableObject {
self.status = status
#if os(macOS)
if status == .invalid || DeviceMetadata.firstTime() {
AppViewModel.WindowDefinition.main.openWindow()
} else {
AppViewModel.WindowDefinition.main.window()?.close()
}
#endif
#if os(macOS)
if status == .invalid || DeviceMetadata.firstTime() {
AppViewModel.WindowDefinition.main.openWindow()
} else {
AppViewModel.WindowDefinition.main.window()?.close()
}
#endif
})
.store(in: &cancellables)
@@ -63,86 +63,71 @@ public class AppViewModel: ObservableObject {
public struct AppView: View {
@ObservedObject var model: AppViewModel
@State private var isSettingsPresented = false
public init(model: AppViewModel) {
self.model = model
}
private var SettingsButton: some View {
Button(action: {
isSettingsPresented = true
}) {
Label("Settings", systemImage: "gear")
}
.disabled(model.status == .invalid)
}
@ViewBuilder
public var body: some View {
#if os(iOS)
NavigationView {
switch (model.status, model.decision) {
case (nil, _), (_, nil):
ProgressView()
case (.invalid, _):
GrantVPNView(model: GrantVPNViewModel(store: model.store))
case (.disconnected, .notDetermined):
GrantNotificationsView(model: GrantNotificationsViewModel(store: model.store))
case (.disconnected, _):
WelcomeView(model: WelcomeViewModel(store: model.store))
.navigationBarItems(trailing: SettingsButton)
case (_, _):
SessionView(model: SessionViewModel(store: model.store))
.navigationBarItems(trailing: SettingsButton)
}
#if os(iOS)
switch (model.status, model.decision) {
case (nil, _), (_, nil):
ProgressView()
case (.invalid, _):
GrantVPNView(model: GrantVPNViewModel(store: model.store))
case (.disconnected, .notDetermined):
GrantNotificationsView(model: GrantNotificationsViewModel(store: model.store))
case (.disconnected, _):
iOSNavigationView(model: model) {
WelcomeView(model: WelcomeViewModel(store: model.store))
}
.sheet(isPresented: $isSettingsPresented) {
SettingsView(model: SettingsViewModel(store: model.store))
case (_, _):
iOSNavigationView(model: model) {
SessionView(model: SessionViewModel(store: model.store))
}
.navigationViewStyle(StackNavigationViewStyle())
#elseif os(macOS)
switch model.store.status {
case .invalid:
GrantVPNView(model: GrantVPNViewModel(store: model.store))
default:
FirstTimeView()
}
#endif
}
#elseif os(macOS)
switch model.store.status {
case .invalid:
GrantVPNView(model: GrantVPNViewModel(store: model.store))
default:
FirstTimeView()
}
#endif
}
}
#if os(macOS)
public extension AppViewModel {
enum WindowDefinition: String, CaseIterable {
case main
case settings
public extension AppViewModel {
enum WindowDefinition: String, CaseIterable {
case main
case settings
public var identifier: String { "firezone-\(rawValue)" }
public var externalEventMatchString: String { rawValue }
public var externalEventOpenURL: URL { URL(string: "firezone://\(rawValue)")! }
public var identifier: String { "firezone-\(rawValue)" }
public var externalEventMatchString: String { rawValue }
public var externalEventOpenURL: URL { URL(string: "firezone://\(rawValue)")! }
public func openWindow() {
if let window = NSApp.windows.first(where: {
$0.identifier?.rawValue.hasPrefix(identifier) ?? false
}) {
// Order existing window front
NSApp.activate(ignoringOtherApps: true)
window.makeKeyAndOrderFront(self)
} else {
// Open new window
NSWorkspace.shared.open(externalEventOpenURL)
}
public func openWindow() {
if let window = NSApp.windows.first(where: {
$0.identifier?.rawValue.hasPrefix(identifier) ?? false
}) {
// Order existing window front
NSApp.activate(ignoringOtherApps: true)
window.makeKeyAndOrderFront(self)
} else {
// Open new window
NSWorkspace.shared.open(externalEventOpenURL)
}
}
public func window() -> NSWindow? {
NSApp.windows.first { window in
if let windowId = window.identifier?.rawValue {
return windowId.hasPrefix(self.identifier)
}
return false
public func window() -> NSWindow? {
NSApp.windows.first { window in
if let windowId = window.identifier?.rawValue {
return windowId.hasPrefix(self.identifier)
}
return false
}
}
}
}
#endif

View File

@@ -0,0 +1,142 @@
//
// ResourceView.swift
//
//
// Created by Jamil Bou Kheir on 5/25/24.
//
import SwiftUI
#if os(iOS)
struct ResourceView: View {
var resource: Resource
@Environment(\.openURL) var openURL
var body: some View {
List {
Section(header: Text("Resource")) {
HStack {
Text("NAME")
.bold()
.font(.system(size: 14))
.foregroundColor(.secondary)
.frame(width: 80, alignment: .leading)
Text(resource.name)
}
.contextMenu {
Button(action: {
copyToClipboard(resource.name)
}) {
Text("Copy name")
Image(systemName: "doc.on.doc")
}
}
HStack {
Text("ADDRESS")
.bold()
.font(.system(size: 14))
.foregroundColor(.secondary)
.frame(width: 80, alignment: .leading)
if let url = URL(string: resource.addressDescription ?? resource.address),
let _ = url.host {
Button(action: {
openURL(url)
}) {
Text(resource.addressDescription ?? resource.address)
.foregroundColor(.blue)
.underline()
.font(.system(size: 16))
.contextMenu {
Button(action: {
copyToClipboard(resource.addressDescription ?? resource.address)
}) {
Text("Copy address")
Image(systemName: "doc.on.doc")
}
}
}
} else {
Text(resource.addressDescription ?? resource.address)
.contextMenu {
Button(action: {
copyToClipboard(resource.addressDescription ?? resource.address)
}) {
Text("Copy address")
Image(systemName: "doc.on.doc")
}
}
}
}
}
if let site = resource.sites.first {
Section(header: Text("Site")) {
HStack {
Text("NAME")
.bold()
.font(.system(size: 14))
.foregroundColor(.secondary)
.frame(width: 80, alignment: .leading)
Text(site.name)
}
.contextMenu {
Button(action: {
copyToClipboard(site.name)
}) {
Text("Copy name")
Image(systemName: "doc.on.doc")
}
}
HStack {
Text("STATUS")
.bold()
.font(.system(size: 14))
.foregroundColor(.secondary)
.frame(width: 80, alignment: .leading)
statusIndicator(for: resource.status)
Text(resource.status.toSiteStatus())
.padding(.leading, 5)
}
.contextMenu {
Button(action: {
copyToClipboard(resource.status.toSiteStatus())
}) {
Text("Copy status")
Image(systemName: "doc.on.doc")
}
}
}
}
}
.listStyle(GroupedListStyle())
.navigationBarTitle("Details", displayMode: .inline)
}
@ViewBuilder
private func statusIndicator(for status: ResourceStatus) -> some View {
HStack {
Circle()
.fill(color(for: status))
.frame(width: 10, height: 10)
}
}
private func color(for status: ResourceStatus) -> Color {
switch status {
case .online:
return .green
case .offline:
return .red
case .unknown:
return .gray
}
}
private func copyToClipboard(_ value: String) {
let pasteboard = UIPasteboard.general
pasteboard.string = value
}
}
#endif

View File

@@ -32,7 +32,7 @@ public final class SessionViewModel: ObservableObject {
.store(in: &cancellables)
// MenuBar has its own observer
#if os(iOS)
#if os(iOS)
store.$status
.receive(on: DispatchQueue.main)
.sink(receiveValue: { [weak self] status in
@@ -41,7 +41,9 @@ public final class SessionViewModel: ObservableObject {
if status == .connected {
store.beginUpdatingResources() { data in
self.resources = try? JSONDecoder().decode([Resource].self, from: data)
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
self.resources = try? decoder.decode([Resource].self, from: data)
}
} else {
store.endUpdatingResources()
@@ -49,14 +51,9 @@ public final class SessionViewModel: ObservableObject {
})
.store(in: &cancellables)
#endif
#endif
}
func signOutButtonTapped() {
Task {
try await store.signOut()
}
}
}
#if os(iOS)
@@ -65,68 +62,34 @@ struct SessionView: View {
@ObservedObject var model: SessionViewModel
var body: some View {
List {
Section(header: Text("Authentication")) {
Group {
if case .connected = model.status {
HStack {
Text("Signed in as")
Spacer()
Text(model.actorName ?? "Unknown user").foregroundColor(.secondary)
}
HStack {
Spacer()
Button("Sign Out") {
model.signOutButtonTapped()
}
Spacer()
}
} else {
Text(model.status?.description ?? "")
}
}
}
if case .connected = model.status {
Section(header: Text("Resources")) {
if let resources = model.resources {
if resources.isEmpty {
Text("No Resources")
} else {
ForEach(resources) { resource in
Menu(
content: {
Button {
copyResourceTapped(resource)
} label: {
Label("Copy Address", systemImage: "doc.on.doc")
}
},
label: {
HStack {
Text(resource.name)
.foregroundColor(.primary)
Spacer()
Text(resource.address)
.foregroundColor(.secondary)
}
})
}
}
} else {
Text("Loading Resources...")
switch model.status {
case .connected:
if let resources = model.resources {
if resources.isEmpty {
Text("No Resources. Contact your admin to be granted access.")
} else {
List(resources) { resource in
NavigationLink(resource.name, destination: ResourceView(resource: resource))
.navigationTitle("All Resources")
}
.listStyle(GroupedListStyle())
}
} else {
Text("Loading Resources...")
}
case .connecting:
Text("Connecting...")
case .disconnecting:
Text("Disconnecting...")
case .reasserting:
Text("No internet connection. Resources will be displayed when your internet connection resumes.")
case .invalid, .none:
Text("VPN permission doesn't seem to be granted.")
case .disconnected:
Text("Signed out. Please sign in again to connect to Resources.")
@unknown default:
Text("Unknown status. Please report this and attach your logs.")
}
.listStyle(GroupedListStyle())
.navigationBarTitleDisplayMode(.inline)
.navigationTitle("Firezone")
}
private func copyResourceTapped(_ resource: Resource) {
let pasteboard = UIPasteboard.general
pasteboard.string = resource.address
}
}
#endif

View File

@@ -1,62 +0,0 @@
//
// SignedOutView.swift
// (c) 2024 Firezone, Inc.
// LICENSE: Apache-2.0
//
import AuthenticationServices
import Combine
import SwiftUI
@MainActor
final class WelcomeViewModel: ObservableObject {
let store: Store
init(store: Store) {
self.store = store
}
func signInButtonTapped() {
Task { await WebAuthSession.signIn(store: store) }
}
}
struct WelcomeView: View {
@ObservedObject var model: WelcomeViewModel
// Debounce button taps
@State private var tapped = false
var body: some View {
VStack(
alignment: .center,
content: {
// TODO: Same screen as Windows app
Spacer()
Image("LogoText")
.resizable()
.scaledToFit()
.frame(maxWidth: 600)
.padding(.horizontal, 10)
Spacer()
Button("Sign in") {
if !tapped {
tapped = true
DispatchQueue.main.async {
model.signInButtonTapped()
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
tapped = false
}
}
}
.disabled(tapped)
.buttonStyle(.borderedProminent)
.controlSize(.large)
Spacer()
})
}
}

View File

@@ -0,0 +1,65 @@
//
// WelcomeView.swift
// (c) 2024 Firezone, Inc.
// LICENSE: Apache-2.0
//
import AuthenticationServices
import Combine
import SwiftUI
@MainActor
final class WelcomeViewModel: ObservableObject {
let store: Store
init(store: Store) {
self.store = store
}
func signInButtonTapped() {
Task { await WebAuthSession.signIn(store: store) }
}
}
struct WelcomeView: View {
@ObservedObject var model: WelcomeViewModel
// Debounce button taps
@State private var tapped = false
var body: some View {
VStack(
alignment: .center,
content: {
Spacer()
Image("LogoText")
.resizable()
.scaledToFit()
.frame(maxWidth: 300)
.padding(.horizontal, 10)
.padding(.vertical, 10)
Text("""
Welcome to Firezone.
Sign in to access Resources.
""").multilineTextAlignment(.center)
.padding(.bottom, 10)
Button("Sign in") {
if !tapped {
tapped = true
DispatchQueue.main.async {
model.signInButtonTapped()
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
tapped = false
}
}
}
.disabled(tapped)
.buttonStyle(.borderedProminent)
.controlSize(.large)
Spacer()
})
}
}

View File

@@ -0,0 +1,84 @@
//
// iOSNavigationView.swift
//
//
// Created by Jamil Bou Kheir on 5/25/24.
//
// A View that contains common elements, intended to be inherited from.
import SwiftUI
#if os(iOS)
struct iOSNavigationView<Content: View>: View {
@State private var isSettingsPresented = false
@ObservedObject var model: AppViewModel
@Environment(\.openURL) var openURL
let content: Content
init(model: AppViewModel, @ViewBuilder content: () -> Content) {
self.model = model
self.content = content()
}
var body: some View {
NavigationView {
content
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(leading: AuthMenu)
.navigationBarItems(trailing: SettingsButton)
}
.sheet(isPresented: $isSettingsPresented) {
SettingsView(model: SettingsViewModel(store: model.store))
}
.navigationViewStyle(StackNavigationViewStyle())
}
private var SettingsButton: some View {
Button(action: {
isSettingsPresented = true
}) {
Label("Settings", systemImage: "gear")
}
.disabled(model.status == .invalid)
}
private var AuthMenu: some View {
Menu {
if model.status == .connected {
Text("Signed in as \(model.store.actorName ?? "Unknown user")")
Button(action: {
signOutButtonTapped()
}) {
Label("Sign out", systemImage: "rectangle.portrait.and.arrow.right")
}
} else {
Button(action: {
Task { await WebAuthSession.signIn(store: model.store) }
}) {
Label("Sign in", systemImage: "person.crop.circle.fill.badge.plus")
}
}
Divider()
Button(action: {
openURL(URL(string: "https://www.firezone.dev/support?utm_source=ios-client")!)
}) {
Label("Support...", systemImage: "safari")
}
Button(action: {
openURL(URL(string: "https://www.firezone.dev/kb?utm_source=ios=client")!)
}) {
Label("Documentation...", systemImage: "safari")
}
} label: {
Image(systemName: "person.circle")
}
}
private func signOutButtonTapped() {
Task {
try await model.store.signOut()
}
}
}
#endif