[fractal/fractal-next] session: Use crosssigning to verify new sessions



commit 3dea9a339860cf5a0a742ab833e8f6bba6f39093
Author: Julian Sparber <julian sparber net>
Date:   Fri Oct 8 16:58:52 2021 +0200

    session: Use crosssigning to verify new sessions

 Cargo.lock                                         |   1 +
 Cargo.toml                                         |   1 +
 .../icons/scalable/status/other-device.svg         | 435 ++++++++++
 .../icons/scalable/status/setup-complete.svg       | 437 ++++++++++
 data/resources/icons/scalable/status/welcome.svg   | 903 ++++++++++++++++-----
 data/resources/resources.gresource.xml             |   4 +
 data/resources/style.css                           |  12 +-
 data/resources/ui/pill.ui                          |   2 +-
 data/resources/ui/session-verification.ui          | 507 ++++++++++++
 data/resources/ui/verification-emoji.ui            |  24 +
 po/POTFILES.in                                     |   9 +
 src/application.rs                                 |   2 +-
 src/contrib/mod.rs                                 |   3 +
 src/contrib/qr_code.rs                             | 313 +++++++
 src/login.rs                                       |  66 +-
 src/main.rs                                        |   1 +
 src/meson.build                                    |   6 +
 src/session/mod.rs                                 | 170 +++-
 src/session/verification/emoji.rs                  |  58 ++
 src/session/verification/identity_verification.rs  | 548 +++++++++++++
 src/session/verification/mod.rs                    |   9 +
 src/session/verification/session_verification.rs   | 424 ++++++++++
 src/session/verification/to_device_handler.rs      |  95 +++
 src/window.rs                                      |  10 +-
 24 files changed, 3764 insertions(+), 276 deletions(-)
---
diff --git a/Cargo.lock b/Cargo.lock
index 5842ad97..5a29bd53 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -740,6 +740,7 @@ dependencies = [
  "matrix-sdk",
  "mime",
  "once_cell",
+ "qrcode",
  "rand 0.8.4",
  "secret-service",
  "serde_json",
diff --git a/Cargo.toml b/Cargo.toml
index 9ca43780..82fcbe53 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -27,6 +27,7 @@ html2pango = "0.4"
 futures = "0.3"
 rand = "0.8"
 indexmap = "1.6.2"
+qrcode = "0.12.0"
 
 [dependencies.sourceview]
 package = "sourceview5"
diff --git a/data/resources/icons/scalable/status/other-device.svg 
b/data/resources/icons/scalable/status/other-device.svg
new file mode 100644
index 00000000..b2596e77
--- /dev/null
+++ b/data/resources/icons/scalable/status/other-device.svg
@@ -0,0 +1,435 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   width="340"
+   height="200"
+   viewBox="0 0 89.958331 52.916668"
+   version="1.1"
+   id="svg8662"
+   xmlns:xlink="http://www.w3.org/1999/xlink";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:dc="http://purl.org/dc/elements/1.1/";>
+  <defs
+     id="defs8656">
+    <linearGradient
+       id="linearGradient104655">
+      <stop
+         style="stop-color:#3d3846;stop-opacity:1"
+         offset="0"
+         id="stop104651" />
+      <stop
+         style="stop-color:#5e5c64;stop-opacity:1"
+         offset="1"
+         id="stop104653" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient82129">
+      <stop
+         style="stop-color:#62a0ea;stop-opacity:1;"
+         offset="0"
+         id="stop82125" />
+      <stop
+         style="stop-color:#c061cb;stop-opacity:1"
+         offset="1"
+         id="stop82127" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient70265">
+      <stop
+         style="stop-color:#99c1f1;stop-opacity:1"
+         offset="0"
+         id="stop70261" />
+      <stop
+         style="stop-color:#dc8add;stop-opacity:1"
+         offset="1"
+         id="stop70263" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient56117">
+      <stop
+         style="stop-color:#3d3846;stop-opacity:1"
+         offset="0"
+         id="stop56113" />
+      <stop
+         style="stop-color:#77767b;stop-opacity:1"
+         offset="1"
+         id="stop56115" />
+    </linearGradient>
+    <linearGradient
+       xlink:href="#linearGradient70265"
+       id="linearGradient39055"
+       x1="34.925"
+       y1="35.057291"
+       x2="67.943703"
+       y2="35.057289"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(6.6145842)" />
+    <linearGradient
+       xlink:href="#linearGradient82129"
+       id="linearGradient82131"
+       x1="11.200694"
+       y1="33.249307"
+       x2="32.54375"
+       y2="33.249307"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.5,0,0,1.5,-0.9260416,-15.875)" />
+    <linearGradient
+       xlink:href="#linearGradient82129"
+       id="linearGradient82161"
+       x1="73.554153"
+       y1="31.750002"
+       x2="85.195839"
+       y2="31.750002"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(0,2.6458333)" />
+    <linearGradient
+       xlink:href="#linearGradient56117"
+       id="linearGradient98041"
+       x1="9.260417"
+       y1="46.302082"
+       x2="9.260417"
+       y2="45.243752"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       xlink:href="#linearGradient104655"
+       id="linearGradient98624"
+       gradientUnits="userSpaceOnUse"
+       x1="9.260417"
+       y1="46.302082"
+       x2="9.260417"
+       y2="45.243752"
+       gradientTransform="matrix(0.38333333,0,0,1,30.021389,0)" />
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath99524">
+      <rect
+         
style="fill:#dc8add;fill-opacity:1;stroke-width:2.11667;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:26.4"
+         id="rect99526"
+         width="35.71875"
+         height="26.458332"
+         x="14.552083"
+         y="17.727087" />
+    </clipPath>
+  </defs>
+  <metadata
+     id="metadata8659">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="g77929"
+     transform="translate(-26.223863,0.26458333)">
+    <rect
+       y="10.054167"
+       x="59.29678"
+       height="7.1434927"
+       width="18.520782"
+       id="rect77923"
+       
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#c0bfbc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.00569266;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       rx="1.5874978"
+       ry="1.2245986" />
+    <rect
+       y="8.7312498"
+       x="59.29678"
+       height="7.9372444"
+       width="18.520782"
+       id="rect77925"
+       
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#deddda;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0060006;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       rx="1.5874978"
+       ry="1.3606703" />
+  </g>
+  <g
+     id="g72761"
+     transform="translate(7.9375002,8.4666665)">
+    <g
+       id="g72759">
+      <rect
+         y="7.1435065"
+         x="43.65625"
+         height="9.2604046"
+         width="19.843752"
+         id="rect72755"
+         
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#c0bfbc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.00648149;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+         rx="1.5874978"
+         ry="1.5874978" />
+      <rect
+         y="6.6143403"
+         x="43.65625"
+         height="9.2604046"
+         width="19.843752"
+         id="rect72757"
+         
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#deddda;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.00648149;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+         rx="1.5874978"
+         ry="1.5874978" />
+    </g>
+  </g>
+  <rect
+     y="10.318505"
+     x="59.29678"
+     height="9.2604046"
+     width="18.520782"
+     id="rect72763"
+     
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#813d9c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.00648149;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+     rx="1.5874978"
+     ry="1.5874978" />
+  <rect
+     y="9.78934"
+     x="59.29678"
+     height="9.2604046"
+     width="18.520782"
+     id="rect72765"
+     
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#c061cb;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.00648149;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+     rx="1.5874978"
+     ry="1.5874978" />
+  <g
+     transform="matrix(0.19843723,0,0,0.19843723,839.155,59.163211)"
+     id="g72771"
+     style="stroke-width:0.666667">
+    <path
+       
style="display:inline;fill:#62a0ea;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
+       d="m -3989.3337,-232.71069 c -4.4319,0 -8,3.56799 -8,8 v 27.89844 c 0,4.43202 3.5681,8 8,8 h 22.9999 
l 12.6667,12.66667 v -12.66667 h 48.3335 c 4.432,0 8,-3.56798 8,-8 v -27.89844 c 0,-4.43201 -3.568,-8 -8,-8 z"
+       id="path72767" />
+    <path
+       id="path72769"
+       d="m -3997.3337,-198.81225 v 2 c 0,4.43202 3.5681,8 8,8 h 22.9999 l 12.6667,12.66667 v -2 l 
-12.6667,-12.66667 h -22.9999 c -4.4319,0 -8,-3.56798 -8,-8 z m 100.0001,0 c 0,4.43202 -3.568,8 -8,8 h 
-48.3335 v 2 h 48.3335 c 4.432,0 8,-3.56798 8,-8 z"
+       
style="display:inline;fill:#3584e4;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
 />
+  </g>
+  <g
+     transform="matrix(0.21083945,0,0,0.13229149,781.89724,63.69551)"
+     id="g72785"
+     style="stroke-width:0.792118">
+    <rect
+       y="-443.48032"
+       x="-3490"
+       height="70"
+       width="101.50459"
+       id="rect72773"
+       
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#1a5fb4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.041718;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       rx="8.7004051"
+       ry="12" />
+    <rect
+       y="-447.48032"
+       x="-3490"
+       height="70"
+       width="101.50459"
+       id="rect72775"
+       
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#1c71d8;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.041718;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       rx="8.7004051"
+       ry="12" />
+    <g
+       id="g72783"
+       style="fill:#99c1f1"
+       transform="translate(8.0037955e-5,0.50000642)">
+      <rect
+         y="-431.98038"
+         x="-3477.4512"
+         height="4.9999967"
+         width="76.406708"
+         id="rect72777"
+         
style="fill:#99c1f1;fill-opacity:1;stroke:none;stroke-width:12.5555;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+      <rect
+         y="-421.98038"
+         x="-3477.4512"
+         height="4.9999967"
+         width="59.607872"
+         id="rect72779"
+         
style="fill:#99c1f1;fill-opacity:1;stroke:none;stroke-width:11.0897;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+      <rect
+         
style="fill:#99c1f1;fill-opacity:1;stroke:none;stroke-width:11.0897;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         id="rect72781"
+         width="37.01963"
+         height="4.9999967"
+         x="-3477.4512"
+         y="-411.98038" />
+    </g>
+  </g>
+  <g
+     transform="matrix(0.19843723,0,0,0.19843723,813.68885,55.062177)"
+     id="g72797"
+     style="stroke-width:0.666667">
+    <path
+       id="path72787"
+       d="m -3913.6666,-217.48035 c -4.4319,0 -8,3.56798 -8,8 v 24 c 0,4.43202 3.5681,8 8,8 h 15.3333 v 
12.66667 l 12.6667,-12.66667 h 45.9999 c 4.432,0 8,-3.56798 8,-8 v -24 c 0,-4.43202 -3.568,-8 -8,-8 z"
+       
style="display:inline;fill:#dc8add;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
 />
+    <path
+       id="path72789"
+       d="m -3921.6666,-187.48035 v 2 c 0,4.43202 3.5681,8 8,8 h 15.3333 v -2 h -15.3333 c -4.4319,0 
-8,-3.56798 -8,-8 z m 89.9999,0 c 0,4.43202 -3.568,8 -8,8 h -45.9999 l -12.6667,12.66667 v 2 l 
12.6667,-12.66667 h 45.9999 c 4.432,0 8,-3.56798 8,-8 z"
+       
style="display:inline;fill:#c061cb;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
 />
+    <rect
+       
style="opacity:1;fill:#c061cb;fill-opacity:1;stroke:none;stroke-width:9.33333;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect72791"
+       width="56.666668"
+       height="3.3333311"
+       x="-3905"
+       y="-207.48033" />
+    <rect
+       
style="opacity:1;fill:#c061cb;fill-opacity:1;stroke:none;stroke-width:9.33333;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect72793"
+       width="56.666668"
+       height="3.3333311"
+       x="-3905"
+       y="-200.81364" />
+    <rect
+       y="-194.14696"
+       x="-3905"
+       height="3.3333311"
+       width="33.333332"
+       id="rect72795"
+       
style="opacity:1;fill:#c061cb;fill-opacity:1;stroke:none;stroke-width:9.33337;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+  </g>
+  <g
+     id="g72817"
+     transform="matrix(0.49999932,0,0,0.49999932,35.483542,3.5716243)">
+    <g
+       id="g72813"
+       transform="matrix(0.26458333,0,0,0.26458333,939.27082,123.15833)">
+      <g
+         style="stroke-width:0.666667"
+         transform="matrix(1.5,0,0,1.5,2465.0001,-75.912199)"
+         id="g72803">
+        <path
+           
style="display:inline;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
+           d="m -3905.3334,-237.71207 c -4.4319,0 -8,3.84245 -8,8.61537 v 29.43586 c 0,4.77294 
3.5681,8.61538 8,8.61538 H -3848 c 4.432,0 8,-3.84244 8,-8.61538 v -29.43586 c 0,-4.77292 -3.568,-8.61537 
-8,-8.61537 z"
+           id="path72799" />
+        <path
+           id="path72801"
+           d="m -3913.3334,-201.04546 v 2 c 0,4.43202 3.5681,8 8,8 h 19 l 12.6667,12.66667 v -2 l 
-12.6667,-12.66667 h -19 c -4.4319,0 -8,-3.56798 -8,-8 z m 73.3334,0 c 0,4.43202 -3.568,8 -8,8 h -25.6667 v 2 
H -3848 c 4.432,0 8,-3.56798 8,-8 z"
+           
style="display:inline;fill:#deddda;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
 />
+      </g>
+      <rect
+         y="-407.48035"
+         x="-3385"
+         height="4.9999967"
+         width="70"
+         id="rect72805"
+         
style="opacity:1;fill:#deddda;fill-opacity:1;stroke:none;stroke-width:14;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+      <rect
+         y="-397.48035"
+         x="-3385"
+         height="4.9999967"
+         width="70"
+         id="rect72807"
+         
style="opacity:1;fill:#deddda;fill-opacity:1;stroke:none;stroke-width:14;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+      <rect
+         
style="opacity:1;fill:#deddda;fill-opacity:1;stroke:none;stroke-width:14;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         id="rect72809"
+         width="59.000008"
+         height="4.9999967"
+         x="-3385"
+         y="-387.48035" />
+      <rect
+         y="-417.48035"
+         x="-3385"
+         height="4.9999967"
+         width="70"
+         id="rect72811"
+         
style="opacity:1;fill:#deddda;fill-opacity:1;stroke:none;stroke-width:14;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+    </g>
+    <path
+       
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="m 54.107302,26.458311 v 5.027084 l -5.027097,-5.027084 z"
+       id="path72815" />
+  </g>
+  <g
+     id="g137252"
+     transform="matrix(0.26458333,0,0,0.26208726,931.33332,121.81302)"
+     style="stroke-width:1.00475" />
+  <g
+     id="g99322"
+     clip-path="url(#clipPath99524)">
+    <rect
+       
style="fill:#3d3846;stroke-width:3.07951;stroke-linecap:round;stroke-linejoin:round;stroke-dashoffset:26.4"
+       id="rect4199"
+       width="33.602081"
+       height="26.987755"
+       x="15.081252"
+       y="20.637245"
+       rx="2.9104166" />
+    <rect
+       
style="fill:url(#linearGradient82131);fill-opacity:1;stroke-width:3.14283;stroke-linecap:round;stroke-linejoin:round;stroke-dashoffset:26.4"
+       id="rect47002"
+       width="32.014584"
+       height="25.135418"
+       x="15.875"
+       y="21.431252"
+       rx="2.1166668" />
+  </g>
+  <g
+     id="g9559"
+     transform="matrix(1.5,0,0,1.5,6.6369485,-1.7057333)"
+     style="fill:#ffffff;fill-opacity:1">
+    <g
+       id="g9536"
+       transform="matrix(0.49999955,0,0,0.49999955,5.4165084,8.0651133)"
+       style="fill:#ffffff;fill-opacity:1">
+      <path
+         
style="display:inline;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00149466px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
+         d="m 17.712178,25.65522 c -0.879455,0 -1.5875,0.762486 -1.5875,1.709612 v 5.444313 c 0,0.94713 
0.708045,1.709614 1.5875,1.709614 h 11.112513 c 0.879475,0 1.5875,-0.762484 1.5875,-1.709614 v -5.444313 c 
0,-0.947126 -0.708025,-1.709612 -1.5875,-1.709612 z"
+         id="path137290-3" />
+      <path
+         
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="m 26.178854,32.534384 v 5.027084 l -5.027097,-5.027084 z"
+         id="path62781-6" />
+    </g>
+  </g>
+  <path
+     
style="fill:none;stroke:url(#linearGradient39055);stroke-width:2.11667;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+     d="m 42.333334,34.395833 h 2.645832 A 2.645834,2.645834 45 0 1 47.625,37.041667 v 1.322917 a 
2.6458333,2.6458333 45 0 0 2.645833,2.645833 h 0 a 2.6458337,2.6458337 135 0 0 2.645834,-2.645834 v -9.260418 
a 2.645833,2.645833 135 0 1 2.645833,-2.645833 2.645833,2.645833 45 0 1 2.645833,2.645833 V 41.010416 A 
2.6458336,2.6458336 45 0 0 60.854167,43.65625 2.6458334,2.6458334 135 0 0 63.5,41.010417 V 31.75 a 
2.6458333,2.6458333 135 0 1 2.645833,-2.645833 h 0 a 2.6458337,2.6458337 45 0 1 2.645834,2.645834 v 1.322915 
a 2.6458336,2.6458336 45 0 0 2.645834,2.645834 h 2.645832"
+     id="path22180" />
+  <rect
+     
style="fill:#3d3846;stroke-width:2.11667;stroke-linecap:round;stroke-linejoin:round;stroke-dashoffset:26.4"
+     id="rect37332"
+     width="13.229169"
+     height="26.458334"
+     x="72.760422"
+     y="21.166668"
+     rx="1.8520825"
+     ry="1.8520826" />
+  <rect
+     
style="fill:url(#linearGradient82161);fill-opacity:1;stroke-width:2.11667;stroke-linecap:round;stroke-linejoin:round;stroke-dashoffset:26.4"
+     id="rect43271"
+     width="11.641685"
+     height="24.870831"
+     x="73.554153"
+     y="21.960421"
+     rx="1.0583326"
+     ry="1.0583326" />
+  <g
+     id="g45487"
+     transform="translate(62.324284,10.923441)"
+     style="fill:#ffffff">
+    <g
+       id="g45485"
+       transform="matrix(0.49999955,0,0,0.49999955,5.4165084,8.0651133)"
+       style="fill:#ffffff">
+      <path
+         
style="display:inline;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00149466px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
+         d="m 17.712178,25.65522 c -0.879455,0 -1.5875,0.762486 -1.5875,1.709612 v 5.444313 c 0,0.94713 
0.708045,1.709614 1.5875,1.709614 h 11.112513 c 0.879475,0 1.5875,-0.762484 1.5875,-1.709614 v -5.444313 c 
0,-0.947126 -0.708025,-1.709612 -1.5875,-1.709612 z"
+         id="path45481" />
+      <path
+         
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="m 26.178854,32.534384 v 5.027084 l -5.027097,-5.027084 z"
+         id="path45483" />
+    </g>
+  </g>
+  <path
+     id="rect95344"
+     d="M 1.0583333,44.185417 H 48.683333 a 2.1166662,2.1166662 135.00001 0 1 -2.116666,2.116666 H 3.175 A 
2.1166664,2.1166664 44.999991 0 1 1.0583333,44.185417 Z"
+     
style="fill:url(#linearGradient98041);fill-opacity:1;stroke-width:3.12455;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:26.4"
 />
+  <path
+     id="path98517"
+     
style="fill:#4b4a50;stroke-width:2.11666;stroke-linecap:round;stroke-linejoin:round;stroke-dashoffset:26.4;fill-opacity:1"
+     d="m 12.594174,44.185417 c 0,0.439737 0.354014,0.79375 0.79375,0.79375 h 6.297072 c 0.439738,0 
0.79375,-0.354013 0.79375,-0.79375 z" />
+  <path
+     id="path98618"
+     d="m 30.427083,44.185417 h 18.25625 a 2.1166662,2.1166662 135.00001 0 1 -2.116666,2.116666 H 32.54375 a 
2.1166664,2.1166664 44.999991 0 1 -2.116667,-2.116666 z"
+     
style="fill:url(#linearGradient98624);fill-opacity:1;stroke-width:1.93453;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:26.4"
 />
+</svg>
diff --git a/data/resources/icons/scalable/status/setup-complete.svg 
b/data/resources/icons/scalable/status/setup-complete.svg
new file mode 100644
index 00000000..71338f9e
--- /dev/null
+++ b/data/resources/icons/scalable/status/setup-complete.svg
@@ -0,0 +1,437 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   width="340"
+   height="200"
+   viewBox="0 0 89.958331 52.916668"
+   version="1.1"
+   id="svg8662"
+   sodipodi:docname="setup-complete.svg"
+   inkscape:version="1.1-rc (52f87abb86, 2021-05-02)"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:xlink="http://www.w3.org/1999/xlink";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:dc="http://purl.org/dc/elements/1.1/";>
+  <sodipodi:namedview
+     id="namedview47"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     objecttolerance="10.0"
+     gridtolerance="10.0"
+     guidetolerance="10.0"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     inkscape:pagecheckerboard="0"
+     showgrid="false"
+     inkscape:zoom="0.32275792"
+     inkscape:cx="-534.45628"
+     inkscape:cy="-774.57432"
+     inkscape:current-layer="svg8662"
+     inkscape:object-paths="true"
+     inkscape:snap-intersection-paths="true"
+     inkscape:snap-midpoints="true"
+     inkscape:snap-smooth-nodes="true"
+     inkscape:snap-bbox="true"
+     inkscape:bbox-nodes="true"
+     inkscape:snap-text-baseline="true">
+    <inkscape:grid
+       type="xygrid"
+       id="grid1470" />
+  </sodipodi:namedview>
+  <defs
+     id="defs8656">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient64686">
+      <stop
+         style="stop-color:#3584e4;stop-opacity:1"
+         offset="0"
+         id="stop64682" />
+      <stop
+         style="stop-color:#99c1f1;stop-opacity:1"
+         offset="1"
+         id="stop64684" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient8145">
+      <stop
+         style="stop-color:#33d17a;stop-opacity:1"
+         offset="0"
+         id="stop8141" />
+      <stop
+         style="stop-color:#8ff0a4;stop-opacity:1"
+         offset="1"
+         id="stop8143" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient8145"
+       id="linearGradient10957"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.1500001,0,0,1.1500001,4215.3556,-2778.2476)"
+       x1="-3292.5"
+       y1="-438.48035"
+       x2="-3272.5"
+       y2="-438.48035" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient64686"
+       id="linearGradient22523"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.695651,0,0,0.695651,130.55973,-999.02559)"
+       x1="428.98032"
+       y1="-3282.5"
+       x2="451.98032"
+       y2="-3282.5" />
+  </defs>
+  <metadata
+     id="metadata8659">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="g20123"
+     transform="translate(1.3229166,1.3229168)">
+    <rect
+       
style="fill:#33d17a;fill-opacity:1;stroke-width:0.79375;stroke-linecap:round;stroke-linejoin:round;stroke-dashoffset:26.4"
+       id="rect13252"
+       width="27.781248"
+       height="14.022916"
+       x="6.614583"
+       y="18.520834"
+       rx="3.175"
+       ry="3.175" />
+    <rect
+       
style="fill:#8ff0a4;fill-opacity:1;stroke-width:0.79375;stroke-linecap:round;stroke-linejoin:round;stroke-dashoffset:26.4"
+       id="rect11780"
+       width="27.781248"
+       height="15.874998"
+       x="6.6145825"
+       y="15.875"
+       rx="3.175"
+       ry="3.175" />
+  </g>
+  <g
+     id="g137252"
+     transform="matrix(0.26458333,0,0,0.26208726,931.33332,121.81302)"
+     style="stroke-width:1.00475" />
+  <g
+     style="stroke-width:0.751809"
+     id="g137258"
+     transform="matrix(0.46810895,0,0,0.26458333,1662.8044,147.5)">
+    <rect
+       
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#c0bfbc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.02834;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       id="rect137254"
+       width="65.000107"
+       height="21.999973"
+       x="3425"
+       y="-399.48032"
+       rx="6.782609"
+       ry="12"
+       transform="scale(-1,1)" />
+    <rect
+       transform="scale(-1,1)"
+       ry="12.000001"
+       rx="6.782609"
+       y="-432.48032"
+       x="3425"
+       height="51.999969"
+       width="65.000107"
+       id="rect137256"
+       
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#deddda;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.02834;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
 />
+  </g>
+  <g
+     id="g4514"
+     transform="matrix(0.68181824,0,0,1,25.676607,9.2604164)"
+     style="stroke-width:1.21106">
+    <rect
+       
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#26a269;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0130465;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       id="rect137266"
+       width="29.104172"
+       height="6.0854096"
+       x="-80.697922"
+       y="23.01874"
+       rx="3.6218512"
+       ry="2.1166666"
+       transform="scale(-1,1)" />
+    <rect
+       transform="scale(-1,1)"
+       ry="2.1166666"
+       rx="3.6218512"
+       y="9.260417"
+       x="-80.697922"
+       height="19.049999"
+       width="29.104172"
+       id="rect137268"
+       
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#57e389;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0130465;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
 />
+  </g>
+  <g
+     transform="matrix(0.396875,0,0,0.396875,1599.4062,118.32708)"
+     id="g137264"
+     style="stroke-width:0.666667">
+    <path
+       sodipodi:nodetypes="sssscccsssss"
+       
style="display:inline;fill:#62a0ea;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
+       d="m -3912.0003,-233.37879 c -4.4319,0 -8,3.56799 -8,8 v 27.89844 c 0,4.43202 3.5681,8 8,8 h 36.3333 
l 12.6667,12.66667 v -12.66667 h 8.3333 c 4.432,0 8,-3.56798 8,-8 v -27.89844 c 0,-4.43201 -3.568,-8 -8,-8 z"
+       id="path137260"
+       inkscape:connector-curvature="0" />
+    <path
+       sodipodi:nodetypes="cssccccsccsccssc"
+       inkscape:connector-curvature="0"
+       id="path137262"
+       d="m -3920.0003,-199.48035 v 2 c 0,4.43202 3.5681,8 8,8 h 36.3333 l 12.6667,12.66667 v -2 l 
-12.6667,-12.66667 h -36.3333 c -4.4319,0 -8,-3.56798 -8,-8 z m 73.3333,0 c 0,4.43202 -3.568,8 -8,8 h -8.3333 
v 2 h 8.3333 c 4.432,0 8,-3.56798 8,-8 z"
+       
style="display:inline;fill:#3584e4;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
 />
+  </g>
+  <g
+     transform="matrix(0.42167947,0,0,0.26458333,1495.4739,124.74584)"
+     id="g137288"
+     style="stroke-width:0.792118">
+    <rect
+       y="-447.48032"
+       x="-3490"
+       height="70"
+       width="87.843063"
+       id="rect137286"
+       
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#1c71d8;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0388092;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       rx="7.5294156"
+       ry="12" />
+    <g
+       id="g4558"
+       style="fill:#99c1f1"
+       transform="translate(8.0037955e-5,0.50000642)">
+      <rect
+         y="-431.98038"
+         x="-3477.4512"
+         height="4.9999967"
+         width="59.607872"
+         id="rect137296-3"
+         
style="fill:#99c1f1;fill-opacity:1;stroke:none;stroke-width:11.0897;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+      <rect
+         y="-421.98038"
+         x="-3477.4512"
+         height="4.9999967"
+         width="59.607872"
+         id="rect137298-6"
+         
style="fill:#99c1f1;fill-opacity:1;stroke:none;stroke-width:11.0897;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+      <rect
+         
style="fill:#99c1f1;fill-opacity:1;stroke:none;stroke-width:11.0897;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         id="rect137300-7"
+         width="37.01963"
+         height="4.9999967"
+         x="-3477.4512"
+         y="-411.98038" />
+    </g>
+  </g>
+  <g
+     transform="matrix(0.396875,0,0,0.396875,1569.6406,110.125)"
+     id="g137282"
+     style="stroke-width:0.666667">
+    <path
+       inkscape:connector-curvature="0"
+       id="path137272"
+       d="m -3913.6666,-217.48035 c -4.4319,0 -8,3.56798 -8,8 v 24 c 0,4.43202 3.5681,8 8,8 h 15.3333 v 
12.66667 l 12.6667,-12.66667 h 45.9999 c 4.432,0 8,-3.56798 8,-8 v -24 c 0,-4.43202 -3.568,-8 -8,-8 z"
+       
style="display:inline;fill:#3d3846;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
+       sodipodi:nodetypes="sssscccsssss" />
+    <path
+       inkscape:connector-curvature="0"
+       id="path137274"
+       d="m -3921.6666,-187.48035 v 2 c 0,4.43202 3.5681,8 8,8 h 15.3333 v -2 h -15.3333 c -4.4319,0 
-8,-3.56798 -8,-8 z m 89.9999,0 c 0,4.43202 -3.568,8 -8,8 h -45.9999 l -12.6667,12.66667 v 2 l 
12.6667,-12.66667 h 45.9999 c 4.432,0 8,-3.56798 8,-8 z"
+       
style="display:inline;fill:#241f31;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
+       sodipodi:nodetypes="cssccsccsccccssc" />
+    <rect
+       
style="opacity:1;fill:#77767b;fill-opacity:1;stroke:none;stroke-width:9.33333;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect137276"
+       width="56.666668"
+       height="3.3333311"
+       x="-3905"
+       y="-207.48033" />
+    <rect
+       
style="opacity:1;fill:#77767b;fill-opacity:1;stroke:none;stroke-width:9.33333;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect137278"
+       width="56.666668"
+       height="3.3333311"
+       x="-3905"
+       y="-200.81364" />
+    <rect
+       y="-194.14696"
+       x="-3905"
+       height="3.3333311"
+       width="33.333332"
+       id="rect137280"
+       
style="opacity:1;fill:#77767b;fill-opacity:1;stroke:none;stroke-width:9.33337;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+  </g>
+  <g
+     id="g50125"
+     transform="translate(-30.427083,-9.7895847)">
+    <rect
+       ry="3.7041636"
+       rx="3.7041638"
+       y="30.691668"
+       x="51.858337"
+       height="7.4083271"
+       width="17.991665"
+       id="rect48478"
+       
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#deddda;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.00997368;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
 />
+    <rect
+       ry="3.7041636"
+       rx="3.7041638"
+       y="29.897917"
+       x="51.858337"
+       height="7.4083271"
+       width="17.991665"
+       id="rect43843"
+       
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.00997368;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
 />
+    <circle
+       
style="fill:#5e5c64;fill-opacity:1;stroke:none;stroke-width:2.11667;stroke-linecap:round;stroke-linejoin:round;stroke-dashoffset:26.4"
+       id="path46775"
+       cx="60.854168"
+       cy="33.602085"
+       r="1.0583339" />
+    <circle
+       
style="fill:#5e5c64;fill-opacity:1;stroke:none;stroke-width:2.11667;stroke-linecap:round;stroke-linejoin:round;stroke-dashoffset:26.4"
+       id="circle46919"
+       cx="57.679169"
+       cy="33.602085"
+       r="1.0583339" />
+    <circle
+       
style="fill:#5e5c64;fill-opacity:1;stroke:none;stroke-width:2.11667;stroke-linecap:round;stroke-linejoin:round;stroke-dashoffset:26.4"
+       id="circle46921"
+       cx="64.02916"
+       cy="33.602085"
+       r="1.0583339" />
+  </g>
+  <g
+     id="g63777"
+     transform="translate(2.6458333,4.4979169)">
+    <g
+       id="g137302"
+       transform="matrix(0.26458333,0,0,0.26458333,939.27082,123.15833)">
+      <g
+         style="stroke-width:0.666667"
+         transform="matrix(1.5,0,0,1.5,2465.0001,-75.912199)"
+         id="g137294">
+        <path
+           sodipodi:nodetypes="sssccssss"
+           
style="display:inline;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
+           d="m -3905.3334,-237.71207 c -4.4319,0 -8,3.84245 -8,8.61537 v 29.43586 c 0,4.77294 
3.5681,8.61538 8,8.61538 H -3848 c 4.432,0 8,-3.84244 8,-8.61538 v -29.43586 c 0,-4.77292 -3.568,-8.61537 
-8,-8.61537 z"
+           id="path137290"
+           inkscape:connector-curvature="0" />
+        <path
+           inkscape:connector-curvature="0"
+           id="path137292"
+           d="m -3913.3334,-201.04546 v 2 c 0,4.43202 3.5681,8 8,8 h 19 l 12.6667,12.66667 v -2 l 
-12.6667,-12.66667 h -19 c -4.4319,0 -8,-3.56798 -8,-8 z m 73.3334,0 c 0,4.43202 -3.568,8 -8,8 h -25.6667 v 2 
H -3848 c 4.432,0 8,-3.56798 8,-8 z"
+           
style="display:inline;fill:#deddda;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
+           sodipodi:nodetypes="cssccccsccsccssc" />
+      </g>
+      <rect
+         y="-407.48035"
+         x="-3385"
+         height="4.9999967"
+         width="70"
+         id="rect137296"
+         
style="opacity:1;fill:#deddda;fill-opacity:1;stroke:none;stroke-width:14;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+      <rect
+         y="-397.48035"
+         x="-3385"
+         height="4.9999967"
+         width="70"
+         id="rect137298"
+         
style="opacity:1;fill:#deddda;fill-opacity:1;stroke:none;stroke-width:14;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+      <rect
+         
style="opacity:1;fill:#deddda;fill-opacity:1;stroke:none;stroke-width:14;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         id="rect137300"
+         width="59.000008"
+         height="4.9999967"
+         x="-3385"
+         y="-387.48035" />
+      <rect
+         y="-417.48035"
+         x="-3385"
+         height="4.9999967"
+         width="70"
+         id="rect8784"
+         
style="opacity:1;fill:#deddda;fill-opacity:1;stroke:none;stroke-width:14;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+    </g>
+    <path
+       
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="m 54.107302,26.458311 v 5.027084 l -5.027097,-5.027084 z"
+       id="path62781" />
+  </g>
+  <g
+     id="g4294"
+     transform="matrix(0.52916666,0,0,0.52916666,-2098.675,240.49589)"
+     style="stroke:#000000">
+    <path
+       
style="fill:#26a269;stroke:#26a269;stroke-width:4;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
+       d="m 4033,-420.48042 h 36 v 14 c 0,15 -6,23 -18,28 -12,-5 -18,-13 -18,-28 z"
+       id="path4286"
+       sodipodi:nodetypes="cccccc" />
+    <path
+       
style="fill:#b0f4bf;fill-opacity:1;stroke:#33d17a;stroke-width:4;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
+       d="m 4033,-422.48042 h 36 v 14 c 0,15 -6,23 -18,28 -12,-5 -18,-13 -18,-28 z"
+       id="path4288"
+       sodipodi:nodetypes="cccccc" />
+    <path
+       style="color:#000000;fill:#26a269;stroke:none"
+       d="m 4061.9165,-408.5693 -12.9165,9.26071 -7.4112,-3.76328 v 1.50546 l 7.4141,7.41407 
12.9141,-12.91407 z"
+       id="path4290"
+       sodipodi:nodetypes="ccccccc" />
+    <path
+       
style="fill:none;stroke:#33d17a;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="m 4043,-404.48042 6,6 11.5,-11.5"
+       id="path4292"
+       sodipodi:nodetypes="ccc" />
+  </g>
+  <g
+     id="g10955"
+     transform="translate(8.7312501,-7.1437498)">
+    <g
+       id="g10953"
+       transform="matrix(0.26458333,0,0,0.26458333,884.23748,129.9052)">
+      <circle
+         
style="opacity:1;fill:#26a269;fill-opacity:1;stroke:none;stroke-width:14;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         id="circle10949"
+         cx="-3282.5"
+         cy="-437.48032"
+         r="11.500001" />
+      <circle
+         r="11.500001"
+         cy="-3282.5"
+         cx="440.48032"
+         id="circle10951"
+         
style="opacity:1;fill:url(#linearGradient10957);fill-opacity:1;stroke:none;stroke-width:14;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         transform="rotate(-90)" />
+    </g>
+  </g>
+  <g
+     id="g22521"
+     transform="translate(54.37188,-1.3229148)">
+    <g
+       id="g22519"
+       transform="matrix(0.26458333,0,0,0.26458333,884.23748,129.9052)">
+      <circle
+         
style="opacity:1;fill:#1a5fb4;fill-opacity:1;stroke:none;stroke-width:9.73912;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         id="circle22515"
+         cx="-3282.5"
+         cy="-433.98029"
+         r="7.999989" />
+      <circle
+         r="7.9999871"
+         cy="-3282.5"
+         cx="436.98032"
+         id="circle22517"
+         
style="opacity:1;fill:url(#linearGradient22523);fill-opacity:1;stroke:none;stroke-width:9.73911;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         transform="rotate(-90)" />
+    </g>
+  </g>
+</svg>
diff --git a/data/resources/icons/scalable/status/welcome.svg 
b/data/resources/icons/scalable/status/welcome.svg
index 915dec91..636f175b 100644
--- a/data/resources/icons/scalable/status/welcome.svg
+++ b/data/resources/icons/scalable/status/welcome.svg
@@ -1,27 +1,85 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/";
-   xmlns:cc="http://creativecommons.org/ns#";
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
-   xmlns:svg="http://www.w3.org/2000/svg";
-   xmlns="http://www.w3.org/2000/svg";
-   xmlns:xlink="http://www.w3.org/1999/xlink";
    width="340"
    height="200"
    viewBox="0 0 89.958331 52.916668"
    version="1.1"
-   id="svg8662">
+   id="svg8662"
+   sodipodi:docname="welcome-export.svg"
+   inkscape:version="1.1-rc (52f87abb86, 2021-05-02)"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:xlink="http://www.w3.org/1999/xlink";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:dc="http://purl.org/dc/elements/1.1/";>
+  <sodipodi:namedview
+     id="namedview47"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     objecttolerance="10.0"
+     gridtolerance="10.0"
+     guidetolerance="10.0"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     inkscape:pagecheckerboard="0"
+     showgrid="false"
+     inkscape:zoom="0.99583586"
+     inkscape:cx="303.76492"
+     inkscape:cy="15.062723"
+     inkscape:current-layer="svg8662"
+     inkscape:object-paths="true"
+     inkscape:snap-intersection-paths="true"
+     inkscape:snap-midpoints="true"
+     inkscape:snap-smooth-nodes="true"
+     inkscape:snap-bbox="true"
+     inkscape:bbox-nodes="true"
+     inkscape:snap-text-baseline="true">
+    <inkscape:grid
+       type="xygrid"
+       id="grid1470" />
+  </sodipodi:namedview>
   <defs
      id="defs8656">
     <linearGradient
-       xlink:href="#linearGradient8161"
-       id="linearGradient8163"
-       x1="-3292.5"
-       y1="-438.48035"
-       x2="-3272.5"
-       y2="-438.48035"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="translate(3720.9803,-2844.0197)" />
+       inkscape:collect="always"
+       id="linearGradient39832">
+      <stop
+         style="stop-color:#3584e4;stop-opacity:1"
+         offset="0"
+         id="stop39828" />
+      <stop
+         style="stop-color:#c061cb;stop-opacity:1"
+         offset="1"
+         id="stop39830" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient24559">
+      <stop
+         style="stop-color:#ed333b;stop-opacity:1;"
+         offset="0"
+         id="stop24555" />
+      <stop
+         style="stop-color:#f66151;stop-opacity:1"
+         offset="1"
+         id="stop24557" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient12858">
+      <stop
+         style="stop-color:#f66151;stop-opacity:1"
+         offset="0"
+         id="stop12854" />
+      <stop
+         style="stop-color:#f6d32d;stop-opacity:1"
+         offset="1"
+         id="stop12856" />
+    </linearGradient>
     <linearGradient
        id="linearGradient8161">
       <stop
@@ -33,15 +91,6 @@
          offset="1"
          id="stop8159" />
     </linearGradient>
-    <linearGradient
-       xlink:href="#linearGradient8145"
-       id="linearGradient8155"
-       x1="-3292.5"
-       y1="-438.48035"
-       x2="-3272.5"
-       y2="-438.48035"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="matrix(1.1500001,0,0,1.1500001,4215.3556,-2778.2476)" />
     <linearGradient
        id="linearGradient8145">
       <stop
@@ -53,6 +102,160 @@
          offset="1"
          id="stop8143" />
     </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient976"
+       id="radialGradient1221"
+       gradientUnits="userSpaceOnUse"
+       cx="64"
+       cy="212"
+       fx="64"
+       fy="212"
+       r="60" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient976">
+      <stop
+         style="stop-color:#f8e45c;stop-opacity:1"
+         offset="0"
+         id="stop972" />
+      <stop
+         style="stop-color:#f5c211;stop-opacity:1"
+         offset="1"
+         id="stop974" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient1117"
+       id="radialGradient1223"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.75,0,54)"
+       cx="36"
+       cy="224"
+       fx="36"
+       fy="224"
+       r="16" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient1117">
+      <stop
+         style="stop-color:#5e5c64;stop-opacity:1"
+         offset="0"
+         id="stop1113" />
+      <stop
+         style="stop-color:#241f31;stop-opacity:1"
+         offset="1"
+         id="stop1115" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient1117"
+       id="radialGradient1225"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.75,40,54)"
+       cx="36"
+       cy="224"
+       fx="36"
+       fy="224"
+       r="16" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient1325">
+      <stop
+         style="stop-color:#f66151;stop-opacity:1"
+         offset="0"
+         id="stop1321" />
+      <stop
+         id="stop1329"
+         offset="0.60000002"
+         style="stop-color:#e6272f;stop-opacity:1;" />
+      <stop
+         style="stop-color:#e01b24;stop-opacity:1"
+         offset="1"
+         id="stop1323" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient976"
+       id="radialGradient1393-0"
+       gradientUnits="userSpaceOnUse"
+       cx="64"
+       cy="212"
+       fx="64"
+       fy="212"
+       r="60"
+       gradientTransform="matrix(0.26458333,0,0,0.26458333,10.972649,-67.464743)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient1325"
+       id="radialGradient1395-6"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.20557447,0,0,0.16449582,14.041109,-16.508601)"
+       cx="52"
+       cy="29.856375"
+       fx="52"
+       fy="29.856375"
+       r="16.084499" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient1325"
+       id="radialGradient1397-9-9"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.20557447,0,0,0.16449582,24.624491,-16.508601)"
+       cx="52"
+       cy="29.856375"
+       fx="52"
+       fy="29.856375"
+       r="16.084499" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient12858"
+       id="linearGradient12860"
+       x1="3467.3748"
+       y1="-422.64157"
+       x2="3467.3748"
+       y2="-383.00339"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient8145"
+       id="linearGradient15076"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.1500001,0,0,1.1500001,4215.3556,-2778.2476)"
+       x1="-3292.5"
+       y1="-438.48035"
+       x2="-3272.5"
+       y2="-438.48035" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient39832"
+       id="radialGradient39804"
+       cx="3496.6987"
+       cy="-406.69202"
+       fx="3496.6987"
+       fy="-406.69202"
+       r="30"
+       gradientTransform="matrix(0.61314223,0,0,0.49927324,-2207.2336,232.19657)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient8161"
+       id="linearGradient40220"
+       x1="428.48035"
+       y1="-3282.5"
+       x2="448.48035"
+       y2="-3282.5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.85000059,0,0,0.85000059,64.271801,-492.37307)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient24559"
+       id="linearGradient63749"
+       gradientUnits="userSpaceOnUse"
+       x1="22.671865"
+       y1="9.260417"
+       x2="22.671865"
+       y2="2.9104166" />
   </defs>
   <metadata
      id="metadata8659">
@@ -62,212 +265,466 @@
         <dc:format>image/svg+xml</dc:format>
         <dc:type
            rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
-        <dc:title></dc:title>
       </cc:Work>
     </rdf:RDF>
   </metadata>
   <g
-     id="layer1">
+     id="g20123">
+    <rect
+       
style="fill:#c01c28;fill-opacity:1;stroke-width:0.79375;stroke-linecap:round;stroke-linejoin:round;stroke-dashoffset:26.4"
+       id="rect13252"
+       width="27.781248"
+       height="18.520834"
+       x="6.614583"
+       y="14.022915"
+       rx="3.175"
+       ry="3.175" />
+    <rect
+       
style="fill:#f66151;fill-opacity:1;stroke-width:0.79375;stroke-linecap:round;stroke-linejoin:round;stroke-dashoffset:26.4"
+       id="rect11780"
+       width="27.781248"
+       height="18.520834"
+       x="6.6145825"
+       y="13.229164"
+       rx="3.175"
+       ry="3.175" />
+  </g>
+  <g
+     transform="matrix(0.42167947,0,0,0.26458333,1495.4739,124.21668)"
+     id="g137288"
+     style="stroke-width:0.792118">
+    <rect
+       y="-447.48032"
+       x="-3490"
+       height="70"
+       width="87.843063"
+       id="rect137286"
+       
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#1c71d8;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0388092;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       rx="7.5294156"
+       ry="12" />
+  </g>
+  <g
+     id="g137252"
+     transform="matrix(0.26458333,0,0,0.26208726,931.33332,121.81302)"
+     style="stroke-width:1.00475" />
+  <g
+     style="stroke-width:0.751809"
+     id="g137258"
+     transform="matrix(0.46810895,0,0,0.26458333,1662.8044,147.5)">
+    <rect
+       
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#e5a50a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.02834;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       id="rect137254"
+       width="65.000107"
+       height="21.999973"
+       x="3425"
+       y="-399.48032"
+       rx="6.782609"
+       ry="12"
+       transform="scale(-1,1)" />
+    <rect
+       transform="scale(-1,1)"
+       ry="12.000001"
+       rx="6.782609"
+       y="-432.48032"
+       x="3425"
+       height="51.999969"
+       width="65.000107"
+       id="rect137256"
+       
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:url(#linearGradient12860);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.02834;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
 />
+  </g>
+  <g
+     transform="matrix(0.396875,0,0,0.396875,1599.4062,118.32708)"
+     id="g137264"
+     style="stroke-width:0.666667">
+    <path
+       sodipodi:nodetypes="sssscccsssss"
+       
style="display:inline;fill:#62a0ea;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
+       d="m -3912.0003,-233.37879 c -4.4319,0 -8,3.56799 -8,8 v 27.89844 c 0,4.43202 3.5681,8 8,8 h 36.3333 
l 12.6667,12.66667 v -12.66667 h 8.3333 c 4.432,0 8,-3.56798 8,-8 v -27.89844 c 0,-4.43201 -3.568,-8 -8,-8 z"
+       id="path137260"
+       inkscape:connector-curvature="0" />
+    <path
+       sodipodi:nodetypes="cssccccsccsccssc"
+       inkscape:connector-curvature="0"
+       id="path137262"
+       d="m -3920.0003,-199.48035 v 2 c 0,4.43202 3.5681,8 8,8 h 36.3333 l 12.6667,12.66667 v -2 l 
-12.6667,-12.66667 h -36.3333 c -4.4319,0 -8,-3.56798 -8,-8 z m 73.3333,0 c 0,4.43202 -3.568,8 -8,8 h -8.3333 
v 2 h 8.3333 c 4.432,0 8,-3.56798 8,-8 z"
+       
style="display:inline;fill:#3584e4;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
 />
+  </g>
+  <rect
+     
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#1c71d8;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0107728;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+     id="rect137266"
+     width="18.520834"
+     height="6.0854096"
+     x="-78.052101"
+     y="25.664574"
+     rx="2.4694443"
+     ry="2.1166666"
+     transform="scale(-1,1)" />
+  <rect
+     transform="scale(-1,1)"
+     ry="2.1166666"
+     rx="2.4694443"
+     y="15.874991"
+     x="-78.052101"
+     height="15.081258"
+     width="18.520834"
+     id="rect137268"
+     
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:url(#radialGradient39804);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0107728;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
 />
+  <g
+     transform="matrix(0.396875,0,0,0.396875,1569.6406,110.125)"
+     id="g137282"
+     style="stroke-width:0.666667">
+    <path
+       inkscape:connector-curvature="0"
+       id="path137272"
+       d="m -3913.6666,-217.48035 c -4.4319,0 -8,3.56798 -8,8 v 24 c 0,4.43202 3.5681,8 8,8 h 15.3333 v 
12.66667 l 12.6667,-12.66667 h 45.9999 c 4.432,0 8,-3.56798 8,-8 v -24 c 0,-4.43202 -3.568,-8 -8,-8 z"
+       
style="display:inline;fill:#3d3846;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
+       sodipodi:nodetypes="sssscccsssss" />
+    <path
+       inkscape:connector-curvature="0"
+       id="path137274"
+       d="m -3921.6666,-187.48035 v 2 c 0,4.43202 3.5681,8 8,8 h 15.3333 v -2 h -15.3333 c -4.4319,0 
-8,-3.56798 -8,-8 z m 89.9999,0 c 0,4.43202 -3.568,8 -8,8 h -45.9999 l -12.6667,12.66667 v 2 l 
12.6667,-12.66667 h 45.9999 c 4.432,0 8,-3.56798 8,-8 z"
+       
style="display:inline;fill:#241f31;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
+       sodipodi:nodetypes="cssccsccsccccssc" />
+    <rect
+       
style="opacity:1;fill:#77767b;fill-opacity:1;stroke:none;stroke-width:9.33333;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect137276"
+       width="56.666668"
+       height="3.3333311"
+       x="-3905"
+       y="-207.48033" />
+    <rect
+       
style="opacity:1;fill:#77767b;fill-opacity:1;stroke:none;stroke-width:9.33333;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect137278"
+       width="56.666668"
+       height="3.3333311"
+       x="-3905"
+       y="-200.81364" />
+    <rect
+       y="-194.14696"
+       x="-3905"
+       height="3.3333311"
+       width="33.333332"
+       id="rect137280"
+       
style="opacity:1;fill:#77767b;fill-opacity:1;stroke:none;stroke-width:9.33337;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+  </g>
+  <g
+     id="g50125"
+     transform="translate(-15.345834,3.4395835)">
+    <rect
+       ry="3.7041636"
+       rx="3.7041638"
+       y="30.691668"
+       x="51.858337"
+       height="7.4083271"
+       width="17.991665"
+       id="rect48478"
+       
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#deddda;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.00997368;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
 />
+    <rect
+       ry="3.7041636"
+       rx="3.7041638"
+       y="29.897917"
+       x="51.858337"
+       height="7.4083271"
+       width="17.991665"
+       id="rect43843"
+       
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.00997368;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
 />
+    <circle
+       
style="fill:#5e5c64;fill-opacity:1;stroke:none;stroke-width:2.11667;stroke-linecap:round;stroke-linejoin:round;stroke-dashoffset:26.4"
+       id="path46775"
+       cx="60.854168"
+       cy="33.602085"
+       r="1.0583339" />
+    <circle
+       
style="fill:#5e5c64;fill-opacity:1;stroke:none;stroke-width:2.11667;stroke-linecap:round;stroke-linejoin:round;stroke-dashoffset:26.4"
+       id="circle46919"
+       cx="57.679169"
+       cy="33.602085"
+       r="1.0583339" />
+    <circle
+       
style="fill:#5e5c64;fill-opacity:1;stroke:none;stroke-width:2.11667;stroke-linecap:round;stroke-linejoin:round;stroke-dashoffset:26.4"
+       id="circle46921"
+       cx="64.02916"
+       cy="33.602085"
+       r="1.0583339" />
+  </g>
+  <g
+     id="g63747">
+    <rect
+       
style="fill:#c01c28;fill-opacity:1;stroke-width:0.793748;stroke-linecap:round;stroke-linejoin:round;stroke-dashoffset:26.4"
+       id="rect63739"
+       width="15.875"
+       height="6.3499999"
+       x="17.4625"
+       y="3.7041667"
+       rx="3.175"
+       ry="3.175" />
+    <rect
+       
style="fill:url(#linearGradient63749);fill-opacity:1;stroke-width:0.793748;stroke-linecap:round;stroke-linejoin:round;stroke-dashoffset:26.4"
+       id="rect63741"
+       width="15.875"
+       height="6.3499999"
+       x="17.4625"
+       y="2.9104166"
+       rx="3.175"
+       ry="3.175" />
+    <text
+       xml:space="preserve"
+       
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.23333px;line-height:1.25;font-family:Cantarell;-inkscape-font-specification:'Cantarell
 
Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#ffffff;stroke-width:0.264583"
+       x="25.548162"
+       y="7.6729164"
+       id="text63745"><tspan
+         sodipodi:role="line"
+         id="tspan63743"
+         
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-size:4.23333px;font-family:Cantarell;-inkscape-font-specification:'Cantarell
 Ultra-Bold';fill:#ffffff;stroke-width:0.264583"
+         x="25.548162"
+         y="7.6729164">999+</tspan></text>
+  </g>
+  <g
+     id="g137308"
+     transform="matrix(0.26458333,0,0,0.26458333,946.54684,145.64793)">
+    <circle
+       
style="opacity:1;fill:#c01c28;fill-opacity:1;stroke:none;stroke-width:14;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="circle137304"
+       cx="-3282.5"
+       cy="-433.98035"
+       r="8.5000057" />
+    <circle
+       r="8.5000057"
+       cy="-3282.5"
+       cx="436.98035"
+       id="circle137306"
+       
style="opacity:1;fill:url(#linearGradient40220);fill-opacity:1;stroke:none;stroke-width:14;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       transform="rotate(-90)" />
+  </g>
+  <g
+     id="g27132"
+     transform="translate(-1.8520832,10.583333)">
+    <g
+       id="g7977-9"
+       transform="matrix(0.26458333,0,0,0.26458333,884.23748,129.9052)">
+      <circle
+         
style="opacity:1;fill:#26a269;fill-opacity:1;stroke:none;stroke-width:14;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         id="circle7973-7"
+         cx="-3282.5"
+         cy="-437.48032"
+         r="11.500001" />
+      <circle
+         r="11.500001"
+         cy="-3282.5"
+         cx="440.48032"
+         id="circle7975-3"
+         
style="opacity:1;fill:url(#linearGradient15076);fill-opacity:1.0;stroke:none;stroke-width:14;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         transform="rotate(-90)" />
+    </g>
+  </g>
+  <g
+     id="g4401"
+     transform="matrix(0.50000001,0,0,0.50000001,8.3343757,26.192195)"
+     style="stroke-width:2">
+    <path
+       id="path2310"
+       style="fill:#1a5fb4;fill-opacity:1;stroke-width:0;stop-color:#000000"
+       d="m 55.850337,32.276064 c -0.557487,0 -1.081579,0.455525 -1.081587,1.058333 v 3.175 h -5.291667 c 
-0.538057,0 -1.058333,0.490969 -1.058333,0.981337 l 1.030943,7.3303 c 0.08548,0.687803 0.556554,1.213363 
1.085723,1.213363 h 6.35 v 0.01188 h 5.291667 v -6.361885 h -3.175 l -1.965772,-7.408333 z" />
+    <path
+       id="path2098"
+       style="fill:#62a0ea;fill-opacity:1;stroke-width:0;stop-color:#000000"
+       d="m 55.850337,31.217732 c -0.557487,0 -1.081579,0.455525 -1.081587,1.058333 v 3.175 h -5.291667 c 
-0.538057,0 -1.058333,0.490969 -1.058333,0.981337 l 1.030943,7.3303 c 0.08548,0.687803 0.556554,1.213363 
1.085723,1.213363 h 6.35 v 0.01188 h 5.291667 V 38.62606 h -3.175 l -1.965772,-7.408333 z" />
+  </g>
+  <g
+     style="display:inline;enable-background:new"
+     id="g1219"
+     transform="matrix(0.13229167,0,0,0.13229167,21.431254,-7.9374947)">
+    <circle
+       
style="opacity:1;vector-effect:none;fill:url(#radialGradient1221);fill-opacity:1;stroke:none;stroke-width:12.8571;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="circle1189"
+       cx="64"
+       cy="236"
+       r="60" />
+    <rect
+       y="206"
+       x="-108"
+       height="4"
+       width="24"
+       id="rect1191"
+       
style="opacity:1;vector-effect:none;fill:#241f31;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       transform="scale(-1,1)"
+       rx="2"
+       ry="2" />
+    <path
+       inkscape:connector-curvature="0"
+       
style="opacity:1;vector-effect:none;fill:#e5a50a;fill-opacity:1;stroke:none;stroke-width:12.8571;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       d="M 123.82422,231.53906 A 60,60 0 0 1 64,288 60,60 0 0 1 4.17578,232.46094 60,60 0 0 0 4,236 a 60,60 
0 0 0 60,60 60,60 0 0 0 60,-60 60,60 0 0 0 -0.17578,-4.46094 z"
+       id="path1193" />
+    <path
+       sodipodi:open="true"
+       sodipodi:end="3.1415927"
+       sodipodi:start="0"
+       sodipodi:ry="7.0068064"
+       sodipodi:rx="7.6309938"
+       sodipodi:cy="236.99103"
+       sodipodi:cx="64"
+       sodipodi:type="arc"
+       id="path1195"
+       
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#3d3846;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       sodipodi:arc-type="arc"
+       d="m 71.630994,236.99103 a 7.6309938,7.0068064 0 0 1 -3.815497,6.06807 7.6309938,7.0068064 0 0 1 
-7.630994,0 7.6309938,7.0068064 0 0 1 -3.815497,-6.06807" />
+    <rect
+       ry="8"
+       rx="8"
+       y="206"
+       x="28"
+       height="24"
+       width="32"
+       id="rect1197"
+       
style="opacity:1;vector-effect:none;fill:#241f31;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+    <path
+       
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#241f31;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="path1199"
+       sodipodi:type="arc"
+       sodipodi:cx="64"
+       sodipodi:cy="-216"
+       sodipodi:rx="8"
+       sodipodi:ry="8"
+       sodipodi:start="0"
+       sodipodi:end="3.1415927"
+       sodipodi:open="true"
+       transform="scale(1,-1)"
+       sodipodi:arc-type="arc"
+       d="m 72,-216 a 8,8 0 0 1 -4,6.9282 8,8 0 0 1 -8,0 A 8,8 0 0 1 56,-216" />
+    <rect
+       
style="opacity:1;vector-effect:none;fill:#241f31;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect1201"
+       width="32"
+       height="24"
+       x="68"
+       y="206"
+       rx="8"
+       ry="8" />
+    <rect
+       
style="opacity:1;vector-effect:none;fill:url(#radialGradient1223);fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect1203"
+       width="32"
+       height="24"
+       x="28"
+       y="204"
+       rx="8"
+       ry="8" />
+    <rect
+       ry="8"
+       rx="8"
+       y="204"
+       x="68"
+       height="24"
+       width="32"
+       id="rect1205"
+       
style="opacity:1;vector-effect:none;fill:url(#radialGradient1225);fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+    <rect
+       ry="2"
+       rx="2"
+       transform="scale(-1,1)"
+       
style="opacity:1;vector-effect:none;fill:#241f31;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect1207"
+       width="24"
+       height="4"
+       x="-44"
+       y="206" />
+  </g>
+  <g
+     id="g63777"
+     transform="translate(-2.6458333)">
     <g
-       id="g9553"
-       transform="translate(-57.074405,-5.2916687)">
+       id="g137302"
+       transform="matrix(0.26458333,0,0,0.26458333,939.27082,123.15833)">
       <g
-         id="g10404"
-         transform="matrix(0.26458333,0,0,0.26458333,-32.869614,144.94489)">
-        <g
-           style="stroke-width:0.751809"
-           id="g8028"
-           transform="matrix(1.7692307,0,0,1,6624.5611,29.65709)">
-          <rect
-             
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#2ec27e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.02834;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-             id="rect8024"
-             width="65.000107"
-             height="21.999973"
-             x="3425"
-             y="-399.48032"
-             rx="6.782609"
-             ry="12"
-             transform="scale(-1,1)" />
-          <rect
-             transform="scale(-1,1)"
-             ry="12.000001"
-             rx="6.782609"
-             y="-432.48032"
-             x="3425"
-             height="51.999969"
-             width="65.000107"
-             id="rect8026"
-             
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#57e389;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.02834;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
 />
-        </g>
-        <g
-           transform="matrix(1.5,0,0,1.5,6384.9459,-80.602744)"
-           id="g4708"
-           style="stroke-width:0.666667">
-          <path
-             
style="display:inline;fill:#1c71d8;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
-             d="m -3912.0003,-233.37879 c -4.4319,0 -8,3.56799 -8,8 v 27.89844 c 0,4.43202 3.5681,8 8,8 h 
36.3333 l 12.6667,12.66667 v -12.66667 h 8.3333 c 4.432,0 8,-3.56798 8,-8 v -27.89844 c 0,-4.43201 -3.568,-8 
-8,-8 z"
-             id="path4704" />
-          <path
-             id="path4706"
-             d="m -3920.0003,-199.48035 v 2 c 0,4.43202 3.5681,8 8,8 h 36.3333 l 12.6667,12.66667 v -2 l 
-12.6667,-12.66667 h -36.3333 c -4.4319,0 -8,-3.56798 -8,-8 z m 73.3333,0 c 0,4.43202 -3.568,8 -8,8 h -8.3333 
v 2 h 8.3333 c 4.432,0 8,-3.56798 8,-8 z"
-             
style="display:inline;fill:#1a5fb4;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
 />
-        </g>
-        <g
-           id="g5222"
-           transform="translate(4089.9459,-0.342944)">
-          <rect
-             
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#f5c211;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0376957;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-             id="rect5218"
-             width="49.99995"
-             height="22.999973"
-             x="3455"
-             y="-420.48032"
-             rx="8"
-             ry="8"
-             transform="scale(-1,1)" />
-          <rect
-             transform="scale(-1,1)"
-             ry="8"
-             rx="8"
-             y="-457.48032"
-             x="3455"
-             height="57.000031"
-             width="49.99995"
-             id="rect5220"
-             
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#f8e45c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0376957;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
 />
-        </g>
-        <g
-           transform="matrix(1.5,0,0,1.5,6272.4459,-121.60274)"
-           id="g4715"
-           style="stroke-width:0.666667">
-          <path
-             id="path4710"
-             d="m -3913.6666,-217.48035 c -4.4319,0 -8,3.56798 -8,8 v 24 c 0,4.43202 3.5681,8 8,8 h 15.3333 
v 12.66667 l 12.6667,-12.66667 h 45.9999 c 4.432,0 8,-3.56798 8,-8 v -24 c 0,-4.43202 -3.568,-8 -8,-8 z"
-             
style="display:inline;fill:#3d3846;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
 />
-          <path
-             id="path4712"
-             d="m -3921.6666,-187.48035 v 2 c 0,4.43202 3.5681,8 8,8 h 15.3333 v -2 h -15.3333 c -4.4319,0 
-8,-3.56798 -8,-8 z m 89.9999,0 c 0,4.43202 -3.568,8 -8,8 h -45.9999 l -12.6667,12.66667 v 2 l 
12.6667,-12.66667 h 45.9999 c 4.432,0 8,-3.56798 8,-8 z"
-             
style="display:inline;fill:#241f31;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
 />
-          <rect
-             
style="opacity:1;vector-effect:none;fill:#77767b;fill-opacity:1;stroke:none;stroke-width:9.33333;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
-             id="rect7985-1"
-             width="56.666668"
-             height="3.3333311"
-             x="-3905"
-             y="-207.48033" />
-          <rect
-             
style="opacity:1;vector-effect:none;fill:#77767b;fill-opacity:1;stroke:none;stroke-width:9.33333;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
-             id="rect7989-4"
-             width="56.666668"
-             height="3.3333311"
-             x="-3905"
-             y="-200.81364" />
-          <rect
-             y="-194.14696"
-             x="-3905"
-             height="3.3333311"
-             width="40"
-             id="rect7991-9"
-             
style="opacity:1;vector-effect:none;fill:#77767b;fill-opacity:1;stroke:none;stroke-width:9.33333;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
-        </g>
-        <g
-           transform="matrix(1.5937492,0,0,1,5992.1307,-55.342911)"
-           id="g5032"
-           style="stroke-width:0.792118">
-          <rect
-             
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#c01c28;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0298595;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-             id="rect5026"
-             width="80.000107"
-             height="50.000008"
-             x="3410"
-             y="-447.48035"
-             rx="5.0196104"
-             ry="8"
-             transform="scale(-1,1)" />
-          <rect
-             transform="scale(-1,1)"
-             ry="8"
-             rx="5.0196104"
-             y="-452.48032"
-             x="3410"
-             height="51.999996"
-             width="80.000107"
-             id="rect5028"
-             
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#ed333b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0298595;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
 />
-        </g>
-        <g
-           id="g8139"
-           transform="translate(3889.9459,-65.34288)">
-          <g
-             style="stroke-width:0.666667"
-             transform="matrix(1.5,0,0,1.5,2465.0001,-75.912199)"
-             id="g4721">
-            <path
-               
style="display:inline;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
-               d="m -3905.3334,-234.37879 c -4.4319,0 -8,3.56799 -8,8 v 27.33333 c 0,4.43202 3.5681,8 8,8 h 
19 l 12.6667,12.66667 v -12.66667 H -3848 c 4.432,0 8,-3.56798 8,-8 v -27.33333 c 0,-4.43201 -3.568,-8 -8,-8 
z"
-               id="path4717" />
-            <path
-               id="path4719"
-               d="m -3913.3334,-201.04546 v 2 c 0,4.43202 3.5681,8 8,8 h 19 l 12.6667,12.66667 v -2 l 
-12.6667,-12.66667 h -19 c -4.4319,0 -8,-3.56798 -8,-8 z m 73.3334,0 c 0,4.43202 -3.568,8 -8,8 h -25.6667 v 2 
H -3848 c 4.432,0 8,-3.56798 8,-8 z"
-               
style="display:inline;fill:#deddda;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
 />
-          </g>
-          <rect
-             y="-412.48035"
-             x="-3385"
-             height="4.9999967"
-             width="70"
-             id="rect7985"
-             
style="opacity:1;vector-effect:none;fill:#deddda;fill-opacity:1;stroke:none;stroke-width:14;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
-          <rect
-             y="-402.48035"
-             x="-3385"
-             height="4.9999967"
-             width="70"
-             id="rect7989"
-             
style="opacity:1;vector-effect:none;fill:#deddda;fill-opacity:1;stroke:none;stroke-width:14;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
-          <rect
-             
style="opacity:1;vector-effect:none;fill:#deddda;fill-opacity:1;stroke:none;stroke-width:14;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
-             id="rect7991"
-             width="45"
-             height="4.9999967"
-             x="-3385"
-             y="-392.48035" />
-        </g>
-        <g
-           id="g7871"
-           transform="translate(3912.4459,32.65712)">
-          <circle
-             
style="opacity:1;vector-effect:none;fill:#c01c28;fill-opacity:1;stroke:none;stroke-width:14;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
-             id="circle7867"
-             cx="-3282.5"
-             cy="-435.48035"
-             r="10" />
-          <circle
-             r="10"
-             cy="-3282.5"
-             cx="438.48035"
-             id="circle7869"
-             
style="opacity:1;vector-effect:none;fill:url(#linearGradient8163);fill-opacity:1;stroke:none;stroke-width:14;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
-             transform="rotate(-90)" />
-        </g>
-        <g
-           id="g7977"
-           transform="translate(3676.4459,-6.842912)">
-          <circle
-             
style="opacity:1;vector-effect:none;fill:#26a269;fill-opacity:1;stroke:none;stroke-width:14;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
-             id="circle7973"
-             cx="-3282.5"
-             cy="-437.48032"
-             r="11.500001" />
-          <circle
-             r="11.500001"
-             cy="-3282.5"
-             cx="440.48032"
-             id="circle7975"
-             
style="opacity:1;vector-effect:none;fill:url(#linearGradient8155);fill-opacity:1;stroke:none;stroke-width:14;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
-             transform="rotate(-90)" />
-        </g>
+         style="stroke-width:0.666667"
+         transform="matrix(1.5,0,0,1.5,2465.0001,-75.912199)"
+         id="g137294">
+        <path
+           sodipodi:nodetypes="sssccssss"
+           
style="display:inline;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
+           d="m -3905.3334,-234.37879 c -4.4319,0 -8,3.56799 -8,8 v 27.33333 c 0,4.43202 3.5681,8 8,8 H 
-3848 c 4.432,0 8,-3.56798 8,-8 v -27.33333 c 0,-4.43201 -3.568,-8 -8,-8 z"
+           id="path137290"
+           inkscape:connector-curvature="0" />
+        <path
+           inkscape:connector-curvature="0"
+           id="path137292"
+           d="m -3913.3334,-201.04546 v 2 c 0,4.43202 3.5681,8 8,8 h 19 l 12.6667,12.66667 v -2 l 
-12.6667,-12.66667 h -19 c -4.4319,0 -8,-3.56798 -8,-8 z m 73.3334,0 c 0,4.43202 -3.568,8 -8,8 h -25.6667 v 2 
H -3848 c 4.432,0 8,-3.56798 8,-8 z"
+           
style="display:inline;fill:#deddda;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.00753214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
+           sodipodi:nodetypes="cssccccsccsccssc" />
       </g>
+      <rect
+         y="-412.48035"
+         x="-3385"
+         height="4.9999967"
+         width="70"
+         id="rect137296"
+         
style="opacity:1;fill:#deddda;fill-opacity:1;stroke:none;stroke-width:14;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+      <rect
+         y="-402.48035"
+         x="-3385"
+         height="4.9999967"
+         width="70"
+         id="rect137298"
+         
style="opacity:1;fill:#deddda;fill-opacity:1;stroke:none;stroke-width:14;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 />
+      <rect
+         
style="opacity:1;fill:#deddda;fill-opacity:1;stroke:none;stroke-width:14;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         id="rect137300"
+         width="59.000008"
+         height="4.9999967"
+         x="-3385"
+         y="-392.48035" />
     </g>
+    <path
+       
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="m 54.107302,26.458311 v 5.027084 l -5.027097,-5.027084 z"
+       id="path62781" />
+  </g>
+  <g
+     id="g1760-2"
+     transform="matrix(0.24999998,0,0,0.24999998,54.891299,38.678132)">
+    <circle
+       
style="display:inline;fill:url(#radialGradient1393-0);fill-opacity:1;stroke:none;stroke-width:3.40177;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;enable-background:new"
+       id="circle1367-0"
+       cx="27.905983"
+       cy="-5.023077"
+       r="15.875" />
+    <path
+       inkscape:connector-curvature="0"
+       
style="display:inline;fill:#e5a50a;fill-opacity:1;stroke:none;stroke-width:3.40177;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;enable-background:new"
+       d="M 43.734473,-6.2033675 A 15.875,15.875 0 0 1 27.905982,8.735256 15.875,15.875 0 0 1 
12.077491,-5.9594534 a 15.875,15.875 0 0 0 -0.04651,0.9363763 15.875,15.875 0 0 0 15.875,15.8750001 
15.875,15.875 0 0 0 15.875,-15.8750001 15.875,15.875 0 0 0 -0.04651,-1.1802904 z"
+       id="path1369-2" />
+    <path
+       sodipodi:open="true"
+       sodipodi:end="3.1415927"
+       sodipodi:start="0"
+       sodipodi:ry="1.8538842"
+       sodipodi:rx="2.0190337"
+       sodipodi:cy="-4.7608676"
+       sodipodi:cx="27.905983"
+       sodipodi:type="arc"
+       id="path1371-3"
+       
style="display:inline;opacity:1;fill:none;fill-opacity:1;stroke:#3d3846;stroke-width:1.05833;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;enable-background:new"
+       sodipodi:arc-type="arc"
+       d="m 29.925017,-4.7608676 a 2.0190337,1.8538842 0 0 1 -1.009517,1.6055108 2.0190337,1.8538842 0 0 1 
-2.019034,0 2.0190337,1.8538842 0 0 1 -1.009517,-1.6055108" />
+    <path
+       inkscape:connector-curvature="0"
+       id="path1373-7"
+       d="m 24.707728,-13.490777 a 2.1168783,2.1168783 0 0 0 -1.459859,0.642337 l -0.620118,0.620117 
-0.620117,-0.620117 a 2.1168783,2.1168783 0 0 0 -1.51877,-0.641302 2.1168783,2.1168783 0 0 0 
-1.474329,3.6349196 l 2.865975,2.8659745 v -0.00212 a 1.0583333,1.0583333 0 0 0 1.49655,0 l 
2.863907,-2.8639082 A 2.1168783,2.1168783 0 0 0 24.707728,-13.49083 Z"
+       
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#radialGradient1395-6);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.23333;stro
 
ke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
 />
+    <path
+       inkscape:connector-curvature="0"
+       
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#radialGradient1397-9-9);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.23333;st
 
roke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       d="m 35.291109,-13.490777 a 2.1168783,2.1168783 0 0 0 -1.459859,0.642337 l -0.620118,0.620117 
-0.620117,-0.620117 a 2.1168783,2.1168783 0 0 0 -1.51877,-0.641302 2.1168783,2.1168783 0 0 0 
-1.474329,3.6349196 l 2.865975,2.8659745 v -0.00212 a 1.0583333,1.0583333 0 0 0 1.49655,0 l 
2.863908,-2.8639082 a 2.1168783,2.1168783 0 0 0 -1.53324,-3.6359539 z"
+       id="path1375-3-5" />
+    <path
+       id="path1377-9"
+       d="m 18.412505,-11.77202 a 2.1168783,2.1168783 0 0 0 0.60203,1.9171976 l 2.865975,2.8659745 v 
-0.00212 a 1.0583333,1.0583333 0 0 0 1.49655,0 l 2.863907,-2.8639109 a 2.1168783,2.1168783 0 0 0 
0.604615,-1.9166782 2.1168783,2.1168783 0 0 1 -0.604615,1.122929 l -2.863907,2.8639074 a 1.0583333,1.0583333 
0 0 1 -1.49655,0 v 0.00212 l -2.865975,-2.8659714 a 2.1168783,2.1168783 0 0 1 -0.60203,-1.123448 z"
+       
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#c01c28;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.23333;stroke-linecap:round;st
 
roke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       inkscape:connector-curvature="0" />
+    <path
+       id="path1379-6-2"
+       d="m 28.995838,-11.77202 a 2.1168783,2.1168783 0 0 0 0.60203,1.9171976 l 2.865975,2.8659745 v 
-0.00212 a 1.0583333,1.0583333 0 0 0 1.49655,0 l 2.863908,-2.8639109 a 2.1168783,2.1168783 0 0 0 
0.604614,-1.9166782 2.1168783,2.1168783 0 0 1 -0.604614,1.122929 l -2.863908,2.8639074 a 1.0583333,1.0583333 
0 0 1 -1.49655,0 v 0.00212 l -2.865975,-2.8659714 a 2.1168783,2.1168783 0 0 1 -0.60203,-1.123448 z"
+       
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#c01c28;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.23333;stroke-linecap:round;st
 
roke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       inkscape:connector-curvature="0" />
   </g>
 </svg>
diff --git a/data/resources/resources.gresource.xml b/data/resources/resources.gresource.xml
index 60e0b34e..a2c82cc5 100644
--- a/data/resources/resources.gresource.xml
+++ b/data/resources/resources.gresource.xml
@@ -40,6 +40,8 @@
     <file compressed="true" preprocess="xml-stripblanks" 
alias="account-settings-devices-page.ui">ui/account-settings-devices-page.ui</file>
     <file compressed="true" preprocess="xml-stripblanks" 
alias="components-loading-listbox-row.ui">ui/components-loading-listbox-row.ui</file>
     <file compressed="true" preprocess="xml-stripblanks" alias="room-creation.ui">ui/room-creation.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks" 
alias="session-verification.ui">ui/session-verification.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks" 
alias="verification-emoji.ui">ui/verification-emoji.ui</file>
     <file compressed="true">style.css</file>
     <file preprocess="xml-stripblanks">icons/scalable/actions/send-symbolic.svg</file>
     <file preprocess="xml-stripblanks">icons/scalable/status/welcome.svg</file>
@@ -47,5 +49,7 @@
     <file preprocess="xml-stripblanks">icons/scalable/status/explore-symbolic.svg</file>
     <file preprocess="xml-stripblanks">icons/scalable/status/devices-symbolic.svg</file>
     <file preprocess="xml-stripblanks">icons/scalable/status/verified-symbolic.svg</file>
+    <file preprocess="xml-stripblanks">icons/scalable/status/setup-complete.svg</file>
+    <file preprocess="xml-stripblanks">icons/scalable/status/other-device.svg</file>
   </gresource>
 </gresources>
diff --git a/data/resources/style.css b/data/resources/style.css
index c2642de1..45a58304 100644
--- a/data/resources/style.css
+++ b/data/resources/style.css
@@ -45,13 +45,13 @@
   padding: 12px 40px;
 }
 
-.pill {
+.inline-pill {
   border-radius: 9999px;
   background-color: alpha(@theme_text_color, 0.1);
   padding-right: 6px;
 }
 
-.app-notification .pill {
+.app-notification .inline-pill {
   background-color: alpha(@theme_bg_color, 0.2);
 }
 
@@ -71,6 +71,10 @@
   min-width: 250px;
 }
 
+.session-verification .text-button {
+ min-width: 200px;
+}
+
 #devnotice {
   background-color: lighter(alpha(@warning_color, 0.3));
   color: shade(@warning_color, 0.5);
@@ -244,3 +248,7 @@ button.cutout:hover {
 button.cutout:active {
   background-color: #c4c5c4;
 }
+
+.emoji {
+  font-size: 2em;
+}
diff --git a/data/resources/ui/pill.ui b/data/resources/ui/pill.ui
index 0f57182a..08c3d298 100644
--- a/data/resources/ui/pill.ui
+++ b/data/resources/ui/pill.ui
@@ -4,7 +4,7 @@
     <property name="focusable">True</property>
     <property name="valign">center</property>
     <style>
-      <class name="pill"/>
+      <class name="inline-pill"/>
     </style>
     <child>
       <object class="GtkBox">
diff --git a/data/resources/ui/session-verification.ui b/data/resources/ui/session-verification.ui
new file mode 100644
index 00000000..5bcb90e9
--- /dev/null
+++ b/data/resources/ui/session-verification.ui
@@ -0,0 +1,507 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="SessionVerification" parent="AdwBin">
+    <style>
+      <class name="session-verification"/>
+    </style>
+    <child>
+      <object class="GtkBox">
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkHeaderBar">
+            <property name="show-title-buttons">True</property>
+            <style>
+              <class name="flat"/>
+            </style>
+            <child type="start">
+              <object class="GtkButton">
+                <property name="visible">True</property>
+                <property name="icon-name">go-previous-symbolic</property>
+                <property name="action-name">verification.previous</property>
+                <style>
+                  <class name="circular"/>
+                </style>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkStack" id="main_stack">
+            <property name="transition-type">crossfade</property>
+            <property name="vexpand">True</property>
+            <property name="margin-top">24</property>
+            <property name="margin-bottom">24</property>
+            <child>
+              <object class="GtkStackPage">
+                <property name="name">wait-for-device</property>
+                <property name="child">
+                  <object class="AdwClamp">
+                    <property name="maximum-size">400</property>
+                    <property name="tightening-threshold">300</property>
+                    <property name="child">
+                      <object class="GtkBox">
+                        <property name="orientation">vertical</property>
+                        <property name="spacing">18</property>
+                        <property name="valign">center</property>
+                        <property name="halign">center</property>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="label" translatable="yes">Get Another Device</property>
+                            <style>
+                              <class name="title-1"/>
+                            </style>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="label" translatable="yes">Accept the verification request from 
another session or device.</property>
+                            <property name="wrap">True</property>
+                            <property name="wrap-mode">word-char</property>
+                            <property name="justify">center</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkPicture">
+                            <property 
name="file">resource:///org/gnome/FractalNext/icons/scalable/status/other-device.svg</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="label" translatable="yes">No other devices logged into this 
account?</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkButton">
+                            <property name="label" translatable="yes">Recovery</property>
+                            <property name="halign">center</property>
+                            <property name="action-name">verification.show-recovery</property>
+                            <style>
+                              <class name="pill"/>
+                            </style>
+                          </object>
+                        </child>
+                      </object>
+                    </property>
+                  </object>
+                </property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkStackPage">
+                <property name="name">bootstrap</property>
+                <property name="child">
+                  <object class="AdwClamp">
+                    <property name="maximum-size">400</property>
+                    <property name="tightening-threshold">300</property>
+                    <property name="child">
+                      <object class="GtkBox">
+                        <property name="orientation">vertical</property>
+                        <property name="spacing">18</property>
+                        <property name="valign">center</property>
+                        <property name="halign">center</property>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="label" translatable="yes">Setup Encryption Identity</property>
+                            <style>
+                              <class name="title-1"/>
+                            </style>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="label" translatable="yes">You need to setup an encryption 
identity, since this is the first time you logged into your account.</property>
+                            <property name="wrap">True</property>
+                            <property name="wrap-mode">word-char</property>
+                            <property name="justify">center</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="SpinnerButton" id="bootstrap_button">
+                            <property name="label" translatable="yes">Setup</property>
+                            <property name="halign">center</property>
+                            <style>
+                              <class name="suggested-action"/>
+                              <class name="pill"/>
+                            </style>
+                          </object>
+                        </child>
+                      </object>
+                    </property>
+                  </object>
+                </property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkStackPage">
+                <property name="name">qrcode</property>
+                <property name="child">
+                  <object class="AdwClamp">
+                    <property name="maximum-size">400</property>
+                    <property name="tightening-threshold">300</property>
+                    <property name="child">
+                      <object class="GtkBox">
+                        <property name="orientation">vertical</property>
+                        <property name="spacing">18</property>
+                        <property name="valign">center</property>
+                        <property name="halign">center</property>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="label" translatable="yes">Verify Session</property>
+                            <style>
+                              <class name="title-1"/>
+                            </style>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="label" translatable="yes">Scan this code from another session 
logged into this account</property>
+                            <property name="wrap">True</property>
+                            <property name="wrap-mode">word-char</property>
+                            <property name="justify">center</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="TriQRCode" id="qrcode">
+                            <property name="valign">center</property>
+                            <property name="halign">center</property>
+                            <property name="margin-top">24</property>
+                            <property name="margin-bottom">24</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="label" translatable="yes">Can't scan QR code?</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="SpinnerButton" id="start_emoji_btn">
+                            <property name="label" translatable="yes">Compare Emoji</property>
+                            <property name="halign">center</property>
+                            <style>
+                              <class name="pill"/>
+                            </style>
+                          </object>
+                        </child>
+                      </object>
+                    </property>
+                  </object>
+                </property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkStackPage">
+                <property name="name">emoji</property>
+                <property name="child">
+                  <object class="AdwClamp">
+                    <property name="maximum-size">400</property>
+                    <property name="tightening-threshold">300</property>
+                    <property name="child">
+                      <object class="GtkBox">
+                        <property name="orientation">vertical</property>
+                        <property name="spacing">18</property>
+                        <property name="valign">center</property>
+                        <property name="halign">center</property>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="label" translatable="yes">Verify Session</property>
+                            <style>
+                              <class name="title-1"/>
+                            </style>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="label" translatable="yes">Check if the same emoji appear in the 
same order on the other device</property>
+                            <property name="wrap">True</property>
+                            <property name="wrap-mode">word-char</property>
+                            <property name="justify">center</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkBox" id="emoji_row_1">
+                            <property name="valign">center</property>
+                            <property name="halign">center</property>
+                            <property name="spacing">30</property>
+                            <property name="margin-top">24</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkBox" id="emoji_row_2">
+                            <property name="valign">center</property>
+                            <property name="halign">center</property>
+                            <property name="spacing">30</property>
+                            <property name="margin-bottom">24</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="SpinnerButton" id="emoji_not_match_btn">
+                            <property name="label" translatable="yes">Do Not Match</property>
+                            <property name="halign">center</property>
+                            <style>
+                              <class name="destructive-action"/>
+                              <class name="pill"/>
+                            </style>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="SpinnerButton" id="emoji_match_btn">
+                            <property name="label" translatable="yes">Match</property>
+                            <property name="halign">center</property>
+                            <style>
+                              <class name="suggested-action"/>
+                              <class name="pill"/>
+                            </style>
+                          </object>
+                        </child>
+                      </object>
+                    </property>
+                  </object>
+                </property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkStackPage">
+                <property name="name">recovery</property>
+                <property name="child">
+                  <object class="AdwClamp">
+                    <property name="maximum-size">400</property>
+                    <property name="tightening-threshold">300</property>
+                    <property name="child">
+                      <object class="GtkBox">
+                        <property name="orientation">vertical</property>
+                        <property name="spacing">18</property>
+                        <property name="valign">center</property>
+                        <property name="halign">center</property>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="label" translatable="yes">Recovery</property>
+                            <style>
+                              <class name="title-1"/>
+                            </style>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="label" translatable="yes">Without another device you need a 
recovery passphrase or key to access your messages</property>
+                            <property name="wrap">True</property>
+                            <property name="wrap">True</property>
+                            <property name="wrap-mode">word-char</property>
+                            <property name="justify">center</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkButton">
+                            <property name="label" translatable="yes">Recovery Passphrase</property>
+                            <property name="halign">center</property>
+                            <style>
+                              <class name="pill"/>
+                            </style>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkButton">
+                            <property name="label" translatable="yes">Recovery Key</property>
+                            <property name="halign">center</property>
+                            <style>
+                              <class name="pill"/>
+                            </style>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="label" translatable="yes">If you don't have any of these you can 
rest your identity, but be aware this makes your old messages inacessible forever.</property>
+                            <property name="wrap">True</property>
+                            <property name="wrap-mode">word-char</property>
+                            <property name="justify">center</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkButton">
+                            <property name="halign">center</property>
+                            <property name="label" translatable="yes">Reset Identity</property>
+                            <style>
+                              <class name="destructive-action"/>
+                              <class name="pill"/>
+                            </style>
+                          </object>
+                        </child>
+                      </object>
+                    </property>
+                  </object>
+                </property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkStackPage">
+                <property name="name">recovery-passphrase</property>
+                <property name="child">
+                  <object class="AdwClamp">
+                    <property name="maximum-size">400</property>
+                    <property name="tightening-threshold">300</property>
+                    <property name="child">
+                      <object class="GtkBox">
+                        <property name="orientation">vertical</property>
+                        <property name="spacing">18</property>
+                        <property name="valign">center</property>
+                        <property name="halign">center</property>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="label" translatable="yes">Recovery Passphrase</property>
+                            <style>
+                              <class name="title-1"/>
+                            </style>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="label" translatable="yes">Your Recovery Passphrase was set up 
when you first created this account.</property>
+                            <property name="wrap">True</property>
+                            <property name="wrap-mode">word-char</property>
+                            <property name="justify">center</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="label" translatable="yes">If you opted for a Recovery Key 
instead go back and choose that option.</property>
+                            <property name="wrap">True</property>
+                            <property name="wrap-mode">word-char</property>
+                            <property name="justify">center</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkPasswordEntry">
+                            <property name="placeholder-text" translatable="yes">Passphrase</property>
+                            <property name="show-peek-icon">True</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkButton">
+                            <property name="label" translatable="yes">Next</property>
+                            <property name="halign">center</property>
+                            <style>
+                              <class name="suggested-action"/>
+                            </style>
+                          </object>
+                        </child>
+                      </object>
+                    </property>
+                  </object>
+                </property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkStackPage">
+                <property name="name">recovery-key</property>
+                <property name="child">
+                  <object class="AdwClamp">
+                    <property name="maximum-size">400</property>
+                    <property name="tightening-threshold">300</property>
+                    <property name="child">
+                      <object class="GtkBox">
+                        <property name="orientation">vertical</property>
+                        <property name="spacing">18</property>
+                        <property name="valign">center</property>
+                        <property name="halign">center</property>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="label" translatable="yes">Recovery Key</property>
+                            <style>
+                              <class name="title-1"/>
+                            </style>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="label" translatable="yes">Your Recovery Key was set up when you 
first created this account.</property>
+                            <property name="wrap">True</property>
+                            <property name="wrap-mode">word-char</property>
+                            <property name="justify">center</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="label" translatable="yes">If you opted for a Recovery Passphrase 
instead go back and choose that option.</property>
+                            <property name="wrap">True</property>
+                            <property name="wrap-mode">word-char</property>
+                            <property name="justify">center</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkTextView">
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkListBox">
+
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkButton">
+                            <property name="label" translatable="yes">Next</property>
+                            <property name="halign">center</property>
+                            <style>
+                              <class name="suggested-action"/>
+                            </style>
+                          </object>
+                        </child>
+                      </object>
+                    </property>
+                  </object>
+                </property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkStackPage">
+                <property name="name">completed</property>
+                <property name="child">
+                  <object class="AdwClamp">
+                    <property name="maximum-size">400</property>
+                    <property name="tightening-threshold">300</property>
+                    <property name="child">
+                      <object class="GtkBox">
+                        <property name="orientation">vertical</property>
+                        <property name="spacing">18</property>
+                        <property name="valign">center</property>
+                        <property name="halign">center</property>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="label" translatable="yes">Setup Complete</property>
+                            <style>
+                              <class name="title-1"/>
+                            </style>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkPicture">
+                            <property 
name="file">resource:///org/gnome/FractalNext/icons/scalable/status/setup-complete.svg</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="label" translatable="yes">This session is ready to send and 
receive secure messages</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkButton" id="get_started_button">
+                            <property name="label" translatable="yes">Get Started</property>
+                            <property name="action-name">session.show-content</property>
+                            <property name="halign">center</property>
+                            <style>
+                              <class name="suggested-action"/>
+                              <class name="pill"/>
+                            </style>
+                          </object>
+                        </child>
+                      </object>
+                    </property>
+                  </object>
+                </property>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
+
diff --git a/data/resources/ui/verification-emoji.ui b/data/resources/ui/verification-emoji.ui
new file mode 100644
index 00000000..42ed5752
--- /dev/null
+++ b/data/resources/ui/verification-emoji.ui
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="VerificationEmoji" parent="AdwBin">
+    <property name="valign">center</property>
+    <child>
+      <object class="GtkBox">
+        <property name="spacing">6</property>
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkLabel" id="emoji">
+            <style>
+              <class name="emoji"/>
+            </style>
+          </object>
+        </child>
+        <child>
+          <object class="GtkLabel" id="emoji_name">
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
+
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 04cb0556..99a2c5fe 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -29,6 +29,7 @@ data/resources/ui/login.ui
 data/resources/ui/in-app-notification.ui
 data/resources/ui/room-creation.ui
 data/resources/ui/session.ui
+data/resources/ui/session-verification.ui
 data/resources/ui/shortcuts.ui
 data/resources/ui/sidebar-category-row.ui
 data/resources/ui/sidebar-entry-row.ui
@@ -38,6 +39,7 @@ data/resources/ui/sidebar.ui
 data/resources/ui/spinner-button.ui
 data/resources/ui/pill.ui
 data/resources/ui/user-entry-row.ui
+data/resources/ui/verification-emoji.ui
 data/resources/ui/window.ui
 
 # Rust files
@@ -52,6 +54,8 @@ src/components/in_app_notification.rs
 src/components/mod.rs
 src/components/spinner_button.rs
 src/components/pill.rs
+src/contrib/mod.rs
+src/contrib/qr_code.rs
 src/error.rs
 src/login.rs
 src/main.rs
@@ -94,6 +98,11 @@ src/session/sidebar/mod.rs
 src/session/sidebar/room_row.rs
 src/session/sidebar/row.rs
 src/session/sidebar/selection.rs
+src/session/verification/emoji.rs
+src/session/verification/identity_verification.rs
+src/session/verification/mod.rs
+src/session/verification/session_verification.rs
+src/session/verification/to_device_handler.rs
 src/session/user.rs
 src/utils.rs
 src/window.rs
diff --git a/src/application.rs b/src/application.rs
index 66195760..8374cae4 100644
--- a/src/application.rs
+++ b/src/application.rs
@@ -125,7 +125,7 @@ impl Application {
             self,
             "new-login",
             clone!(@weak self as app => move |_, _| {
-                app.get_main_window().switch_to_login_page();
+                app.get_main_window().switch_to_login_page(true);
             })
         );
 
diff --git a/src/contrib/mod.rs b/src/contrib/mod.rs
new file mode 100644
index 00000000..d5fc7251
--- /dev/null
+++ b/src/contrib/mod.rs
@@ -0,0 +1,3 @@
+mod qr_code;
+
+pub use self::qr_code::{QRCode, QRCodeExt};
diff --git a/src/contrib/qr_code.rs b/src/contrib/qr_code.rs
new file mode 100644
index 00000000..44cbde8c
--- /dev/null
+++ b/src/contrib/qr_code.rs
@@ -0,0 +1,313 @@
+// Taken from https://gitlab.gnome.org/msandova/trinket/-/blob/master/src/qr_code.rs
+// All credit goes to Maximiliano
+use gtk::glib;
+use gtk::prelude::*;
+use gtk::subclass::prelude::*;
+
+use std::convert::TryFrom;
+
+pub(crate) mod imp {
+    use super::*;
+
+    use gtk::{gdk, graphene};
+
+    use once_cell::sync::Lazy;
+    use std::cell::{Cell, RefCell};
+
+    #[derive(Debug, Default)]
+    pub struct QRCode {
+        pub picture: gtk::Picture,
+        pub data: RefCell<QRCodeData>,
+        pub block_size: Cell<u32>,
+    }
+
+    #[glib::object_subclass]
+    impl ObjectSubclass for QRCode {
+        const NAME: &'static str = "TriQRCode";
+        type Type = super::QRCode;
+        type ParentType = gtk::Widget;
+
+        fn new() -> Self {
+            Self {
+                block_size: Cell::new(8),
+                ..Self::default()
+            }
+        }
+    }
+
+    impl ObjectImpl for QRCode {
+        fn constructed(&self, obj: &Self::Type) {
+            self.parent_constructed(obj);
+            obj.add_css_class("qrcode");
+        }
+
+        fn properties() -> &'static [glib::ParamSpec] {
+            static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
+                vec![glib::ParamSpec::new_uint(
+                    "block-size",
+                    "block-size",
+                    "block-size",
+                    1,
+                    u32::MAX,
+                    8,
+                    glib::ParamFlags::READWRITE | glib::ParamFlags::EXPLICIT_NOTIFY,
+                )]
+            });
+            PROPERTIES.as_ref()
+        }
+
+        fn property(&self, obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
+            match pspec.name() {
+                "block-size" => obj.block_size().to_value(),
+                _ => unreachable!(),
+            }
+        }
+
+        fn set_property(
+            &self,
+            obj: &Self::Type,
+            _id: usize,
+            value: &glib::Value,
+            pspec: &glib::ParamSpec,
+        ) {
+            match pspec.name() {
+                "block-size" => obj.set_block_size(value.get().unwrap()),
+                _ => unreachable!(),
+            }
+        }
+    }
+    impl WidgetImpl for QRCode {
+        fn snapshot(&self, widget: &Self::Type, snapshot: &gtk::Snapshot) {
+            let square_width = widget.width() as f32 / self.data.borrow().width as f32;
+            let square_height = widget.height() as f32 / self.data.borrow().height as f32;
+
+            self.data
+                .borrow()
+                .items
+                .iter()
+                .enumerate()
+                .for_each(|(y, line)| {
+                    line.iter().enumerate().for_each(|(x, is_dark)| {
+                        let color = if *is_dark {
+                            widget.style_context().color()
+                        } else {
+                            widget
+                                .style_context()
+                                .lookup_color("background")
+                                .unwrap_or(gdk::RGBA {
+                                    red: 0.0,
+                                    blue: 0.0,
+                                    green: 0.0,
+                                    alpha: 0.0,
+                                })
+                        };
+                        let position = graphene::Rect::new(
+                            (x as f32) * square_width,
+                            (y as f32) * square_height,
+                            square_width,
+                            square_height,
+                        );
+
+                        snapshot.append_color(&color, &position);
+                    });
+                });
+        }
+
+        fn measure(
+            &self,
+            widget: &Self::Type,
+            orientation: gtk::Orientation,
+            for_size: i32,
+        ) -> (i32, i32, i32, i32) {
+            let self_ = imp::QRCode::from_instance(widget);
+
+            let stride = widget.block_size() as i32;
+
+            let minimum = match orientation {
+                gtk::Orientation::Horizontal => self_.data.borrow().width * stride,
+                gtk::Orientation::Vertical => self_.data.borrow().height * stride,
+                _ => unreachable!(),
+            };
+            let natural = std::cmp::max(for_size, minimum);
+            (minimum, natural, -1, -1)
+        }
+    }
+}
+
+glib::wrapper! {
+    /// A widget that display a QR Code.
+    ///
+    /// The QR code of [`QRCode`] is set with the [QRCodeExt::set_bytes()]
+    /// method. It is recommended for a QR Code to have a quiet zone, i.e. a margin of
+    /// four times the value of [`QRCodeExt::block_size()`], in most contexts, widgets
+    /// already count with such a margin.
+    ///
+    /// The code can be themed via css, where a recommended quiet-zone
+    /// can be as a padding:
+    ///
+    /// ```css
+    /// .qrcode {
+    ///     color: black;
+    ///     background: white;
+    ///     padding: 24px;  /* 4 ⨉ block-size */
+    /// }
+    /// ```
+    ///
+    /// **Implements**: [QRCodeExt].
+    pub struct QRCode(ObjectSubclass<imp::QRCode>)
+        @extends gtk::Widget;
+}
+
+impl Default for QRCode {
+    fn default() -> Self {
+        glib::Object::new(&[]).unwrap()
+    }
+}
+
+impl QRCode {
+    /// Creates a new [`QRCode`].
+    pub fn new() -> Self {
+        Self::default()
+    }
+
+    /// Creates a new [`QRCode`] with a QR code generated from `bytes`.
+    pub fn from_bytes(bytes: &[u8]) -> Self {
+        let qrcode = Self::default();
+        qrcode.set_bytes(bytes);
+
+        qrcode
+    }
+}
+
+pub trait QRCodeExt {
+    /// Sets the displayed code of `self` to a QR code generated from `bytes`.
+    fn set_bytes(&self, bytes: &[u8]);
+
+    /// Gets the block size `self`. This determines the size of the the widget.
+    fn block_size(&self) -> u32;
+
+    /// Sets the block size `self`.
+    fn set_block_size(&self, block_size: u32);
+
+    fn connect_block_size_notify<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId;
+
+    /// Set the `QrCode` to be displayed
+    fn set_qrcode(&self, qrcode: qrcode::QrCode);
+}
+
+impl<W: IsA<QRCode>> QRCodeExt for W {
+    fn set_bytes(&self, bytes: &[u8]) {
+        let this = imp::QRCode::from_instance(self.as_ref());
+
+        let data = QRCodeData::try_from(bytes).unwrap_or_else(|_| {
+            glib::g_warning!(None, "Failed to load QRCode from bytes");
+            Default::default()
+        });
+        this.data.replace(data);
+
+        self.as_ref().queue_draw();
+        self.as_ref().queue_resize();
+    }
+
+    fn set_qrcode(&self, qrcode: qrcode::QrCode) {
+        let this = imp::QRCode::from_instance(self.as_ref());
+
+        this.data.replace(QRCodeData::from(qrcode));
+
+        self.as_ref().queue_draw();
+        self.as_ref().queue_resize();
+    }
+
+    fn block_size(&self) -> u32 {
+        let this = imp::QRCode::from_instance(self.as_ref());
+
+        this.block_size.get()
+    }
+
+    fn set_block_size(&self, block_size: u32) {
+        let this = imp::QRCode::from_instance(self.as_ref());
+
+        this.block_size.set(std::cmp::max(block_size, 1));
+        self.notify("block-size");
+        self.as_ref().queue_draw();
+        self.as_ref().queue_resize();
+    }
+
+    fn connect_block_size_notify<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
+        self.connect_notify_local(Some("block-size"), move |this, _| {
+            f(this);
+        })
+    }
+}
+
+impl Default for QRCodeData {
+    fn default() -> Self {
+        Self::try_from("".as_bytes()).unwrap()
+    }
+}
+
+#[derive(Debug, Clone)]
+pub struct QRCodeData {
+    pub width: i32,
+    pub height: i32,
+    pub items: Vec<Vec<bool>>,
+}
+
+impl TryFrom<&[u8]> for QRCodeData {
+    type Error = qrcode::types::QrError;
+
+    fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
+        let code = qrcode::QrCode::new(data)?;
+        let items = code
+            .render::<char>()
+            .quiet_zone(false)
+            .module_dimensions(1, 1)
+            .build()
+            .split('\n')
+            .into_iter()
+            .map(|line| {
+                line.chars()
+                    .into_iter()
+                    .map(|c| !c.is_whitespace())
+                    .collect::<Vec<bool>>()
+            })
+            .collect::<Vec<Vec<bool>>>();
+
+        let height = items.len() as i32;
+        let width = items.len() as i32;
+        let data = Self {
+            width,
+            height,
+            items,
+        };
+
+        Ok(data)
+    }
+}
+
+impl From<qrcode::QrCode> for QRCodeData {
+    fn from(code: qrcode::QrCode) -> Self {
+        let items = code
+            .render::<char>()
+            .quiet_zone(false)
+            .module_dimensions(1, 1)
+            .build()
+            .split('\n')
+            .into_iter()
+            .map(|line| {
+                line.chars()
+                    .into_iter()
+                    .map(|c| !c.is_whitespace())
+                    .collect::<Vec<bool>>()
+            })
+            .collect::<Vec<Vec<bool>>>();
+
+        let height = items.len() as i32;
+        let width = items.len() as i32;
+        Self {
+            width,
+            height,
+            items,
+        }
+    }
+}
diff --git a/src/login.rs b/src/login.rs
index 30f1edb1..2e4e228f 100644
--- a/src/login.rs
+++ b/src/login.rs
@@ -10,6 +10,7 @@ use url::{ParseError, Url};
 mod imp {
     use super::*;
     use glib::subclass::{InitializingObject, Signal};
+    use glib::SignalHandlerId;
     use once_cell::sync::Lazy;
     use std::cell::RefCell;
 
@@ -35,6 +36,9 @@ mod imp {
         pub password_entry: TemplateChild<gtk::PasswordEntry>,
         #[template_child]
         pub back_to_session_button: TemplateChild<gtk::Button>,
+        pub prepered_source_id: RefCell<Option<SignalHandlerId>>,
+        pub logged_out_source_id: RefCell<Option<SignalHandlerId>>,
+        pub ready_source_id: RefCell<Option<SignalHandlerId>>,
     }
 
     #[glib::object_subclass]
@@ -124,7 +128,6 @@ impl Login {
         self.freeze();
 
         let session = Session::new();
-
         self.set_handler_for_prepared_session(&session);
 
         session.login_with_password(
@@ -132,16 +135,16 @@ impl Login {
             username,
             password,
         );
-
         priv_.current_session.replace(Some(session));
     }
 
-    fn clean(&self) {
+    pub fn clean(&self) {
         let priv_ = imp::Login::from_instance(self);
         priv_.homeserver_entry.set_text("");
         priv_.username_entry.set_text("");
         priv_.password_entry.set_text("");
         self.unfreeze();
+        self.drop_session_reference();
     }
 
     fn freeze(&self) {
@@ -180,7 +183,17 @@ impl Login {
     fn drop_session_reference(&self) {
         let priv_ = imp::Login::from_instance(self);
 
-        priv_.current_session.take();
+        if let Some(session) = priv_.current_session.take() {
+            if let Some(id) = priv_.prepered_source_id.take() {
+                session.disconnect(id);
+            }
+            if let Some(id) = priv_.logged_out_source_id.take() {
+                session.disconnect(id);
+            }
+            if let Some(id) = priv_.ready_source_id.take() {
+                session.disconnect(id);
+            }
+        }
     }
 
     pub fn default_widget(&self) -> gtk::Widget {
@@ -194,20 +207,39 @@ impl Login {
     }
 
     fn set_handler_for_prepared_session(&self, session: &Session) {
-        session.connect_prepared(clone!(@weak self as login => move |session, error| {
-            match error {
-                Some(e) => {
-                    login.parent_window().append_error(&e);
+        let priv_ = imp::Login::from_instance(self);
+        priv_
+            .prepered_source_id
+            .replace(Some(session.connect_prepared(
+                clone!(@weak self as login => move |session, error| {
+                    match error {
+                        Some(e) => {
+                            login.parent_window().append_error(&e);
+                            login.unfreeze();
+                        },
+                        None => {
+                            debug!("A new session was prepared");
+                            login.emit_by_name("new-session", &[&session]).unwrap();
+                        }
+                    }
+                }),
+            )));
+
+        priv_.ready_source_id.replace(Some(session.connect_ready(
+            clone!(@weak self as login => move |_| {
+                login.clean();
+            }),
+        )));
+
+        priv_
+            .logged_out_source_id
+            .replace(Some(session.connect_logged_out(
+                clone!(@weak self as login => move |_| {
+                    login.parent_window().switch_to_login_page(false);
+                    login.drop_session_reference();
                     login.unfreeze();
-                },
-                None => {
-                    debug!("A new session was prepared");
-                    login.emit_by_name("new-session", &[&session]).unwrap();
-                    login.clean();
-                }
-            }
-            login.drop_session_reference();
-        }));
+                }),
+            )));
     }
 
     fn parent_window(&self) -> crate::Window {
diff --git a/src/main.rs b/src/main.rs
index d717dc1c..e50ec1c4 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -8,6 +8,7 @@ mod config;
 mod prelude;
 
 mod components;
+mod contrib;
 mod error;
 mod login;
 mod matrix_error;
diff --git a/src/meson.build b/src/meson.build
index 2e74b35f..20ba3b0b 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -20,6 +20,7 @@ run_command(
 
 sources = files(
   'application.rs',
+  'contrib/qr_code.rs',
   'components/avatar.rs',
   'components/auth_dialog.rs',
   'components/context_menu_bin.rs',
@@ -83,6 +84,11 @@ sources = files(
   'session/sidebar/account_switcher/item.rs',
   'session/sidebar/account_switcher/mod.rs',
   'session/sidebar/account_switcher/user_entry.rs',
+  'session/verification/mod.rs',
+  'session/verification/emoji.rs',
+  'session/verification/identity_verification.rs',
+  'session/verification/session_verification.rs',
+  'session/verification/to_device_handler.rs',
 )
 
 custom_target(
diff --git a/src/session/mod.rs b/src/session/mod.rs
index 25a72d24..3e6d01cf 100644
--- a/src/session/mod.rs
+++ b/src/session/mod.rs
@@ -7,6 +7,7 @@ mod room_creation;
 mod room_list;
 mod sidebar;
 mod user;
+mod verification;
 
 use self::account_settings::AccountSettings;
 pub use self::avatar::Avatar;
@@ -16,6 +17,7 @@ pub use self::room_creation::RoomCreation;
 use self::room_list::RoomList;
 use self::sidebar::Sidebar;
 pub use self::user::{User, UserExt};
+pub use self::verification::{IdentityVerification, SessionVerification, ToDeviceHandler};
 
 use crate::secret;
 use crate::secret::StoredSession;
@@ -79,9 +81,11 @@ mod imp {
         pub selected_room: RefCell<Option<Room>>,
         pub selected_content_type: Cell<ContentType>,
         pub is_ready: Cell<bool>,
+        pub logout_on_dispose: Cell<bool>,
         pub info: OnceCell<StoredSession>,
         pub source_id: RefCell<Option<SourceId>>,
         pub sync_tokio_handle: RefCell<Option<JoinHandle<()>>>,
+        pub to_device_handler: OnceCell<ToDeviceHandler>,
     }
 
     #[glib::object_subclass]
@@ -98,7 +102,15 @@ mod imp {
             });
 
             klass.install_action("session.logout", None, move |session, _, _| {
-                session.logout();
+                spawn!(clone!(@weak session => async move {
+                    let priv_ = imp::Session::from_instance(&session);
+                    priv_.logout_on_dispose.set(false);
+                    session.logout().await
+                }));
+            });
+
+            klass.install_action("session.show-content", None, move |session, _, _| {
+                session.show_content();
             });
 
             klass.install_action("session.room-creation", None, move |session, _, _| {
@@ -214,13 +226,14 @@ mod imp {
                         <()>::static_type().into(),
                     )
                     .build(),
+                    Signal::builder("ready", &[], <()>::static_type().into()).build(),
                     Signal::builder("logged-out", &[], <()>::static_type().into()).build(),
                 ]
             });
             SIGNALS.as_ref()
         }
 
-        fn dispose(&self, _obj: &Self::Type) {
+        fn dispose(&self, obj: &Self::Type) {
             if let Some(source_id) = self.source_id.take() {
                 let _ = glib::Source::remove(source_id);
             }
@@ -228,6 +241,10 @@ mod imp {
             if let Some(handle) = self.sync_tokio_handle.take() {
                 handle.abort();
             }
+
+            if self.logout_on_dispose.get() {
+                glib::MainContext::default().block_on(obj.logout());
+            }
         }
     }
     impl WidgetImpl for Session {}
@@ -285,6 +302,8 @@ impl Session {
     }
 
     pub fn login_with_password(&self, homeserver: Url, username: String, password: String) {
+        let priv_ = imp::Session::from_instance(self);
+        priv_.logout_on_dispose.set(true);
         let mut path = glib::user_data_dir();
         path.push(
             &Uuid::new_v4()
@@ -361,6 +380,7 @@ impl Session {
                 .await
                 .map(|_| (client, session))
         });
+
         spawn!(
             glib::PRIORITY_DEFAULT_IDLE,
             clone!(@weak self as obj => async move {
@@ -382,6 +402,11 @@ impl Session {
                 priv_.user.set(user.clone()).unwrap();
                 self.notify("user");
 
+                priv_
+                    .to_device_handler
+                    .set(ToDeviceHandler::new(self))
+                    .unwrap();
+
                 let handle = spawn_tokio!(async move {
                     let display_name = client.display_name().await?;
                     let avatar_url = client.avatar_url().await?;
@@ -407,6 +432,7 @@ impl Session {
                 priv_.info.set(session).unwrap();
 
                 self.room_list().load();
+
                 self.sync();
 
                 None
@@ -465,9 +491,63 @@ impl Session {
     }
 
     fn mark_ready(&self) {
-        let priv_ = &imp::Session::from_instance(self);
-        priv_.stack.set_visible_child(&*priv_.content);
+        let priv_ = imp::Session::from_instance(self);
+        let client = self.client();
+        let user_id = self.user().unwrap().user_id().to_owned();
+
         priv_.is_ready.set(true);
+
+        let has_cross_signing_keys = spawn_tokio!(async move {
+            if let Some(cross_signing_status) = client.cross_signing_status().await {
+                cross_signing_status.has_master
+                    && cross_signing_status.has_self_signing
+                    && cross_signing_status.has_user_signing
+            } else {
+                false
+            }
+        });
+
+        let client = self.client();
+        let need_new_identity = spawn_tokio!(async move {
+            // If there is an error just assume we don't need a new identity since
+            // we will try again during the session verification
+            client
+                .get_user_identity(&user_id)
+                .await
+                .map_or(false, |identity| identity.is_none())
+        });
+
+        spawn!(clone!(@weak self as obj => async move {
+            let priv_ = imp::Session::from_instance(&obj);
+            if !has_cross_signing_keys.await.unwrap() {
+                if need_new_identity.await.unwrap() {
+                    let client = obj.client();
+                    if spawn_tokio!(async move { client.bootstrap_cross_signing(None).await }).await.is_ok() 
{
+                        priv_.stack.set_visible_child(&*priv_.content);
+                        return;
+                    }
+                }
+
+                priv_.logout_on_dispose.set(true);
+
+                let verification = IdentityVerification::new(obj.user().unwrap());
+                let session = SessionVerification::new(&verification, &obj);
+                priv_
+                    .to_device_handler
+                    .get()
+                    .unwrap()
+                    .add_request(verification);
+                priv_
+                    .stack
+                    .add_named(&session, Some("session-verification"));
+                priv_.stack.set_visible_child(&session);
+                session.start_verification();
+
+                return;
+            }
+
+            obj.show_content();
+        }));
     }
 
     fn is_ready(&self) -> bool {
@@ -543,13 +623,31 @@ impl Session {
         .unwrap()
     }
 
+    pub fn connect_ready<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
+        self.connect_local("ready", true, move |values| {
+            let obj = values[0].get::<Self>().unwrap();
+
+            f(&obj);
+
+            None
+        })
+        .unwrap()
+    }
+
     fn handle_sync_response(&self, response: Result<SyncResponse, matrix_sdk::Error>) {
+        let priv_ = imp::Session::from_instance(self);
+
         match response {
             Ok(response) => {
                 if !self.is_ready() {
                     self.mark_ready();
                 }
                 self.room_list().handle_response_rooms(response.rooms);
+                priv_
+                    .to_device_handler
+                    .get()
+                    .unwrap()
+                    .handle_response_to_device(response.to_device);
             }
             Err(error) => {
                 if let matrix_sdk::Error::Http(HttpError::ClientApi(FromHttpResponseError::Http(
@@ -592,36 +690,37 @@ impl Session {
         window.show();
     }
 
-    pub fn logout(&self) {
-        let client = self.client();
+    pub async fn logout(&self) {
+        let priv_ = imp::Session::from_instance(self);
+        self.emit_by_name("logged-out", &[]).unwrap();
+
+        debug!("The session is about to be logout");
 
+        // First stop the verification in progress
+        if let Some(session_verificiation) = priv_.stack.child_by_name("session-verification") {
+            priv_.stack.remove(&session_verificiation);
+        }
+
+        let client = self.client();
         let handle = spawn_tokio!(async move {
             let request = logout::Request::new();
             client.send(request, None).await
         });
 
-        spawn!(
-            glib::PRIORITY_DEFAULT_IDLE,
-            clone!(@weak self as obj => async move {
-                match handle.await.unwrap() {
-                    Ok(_) => obj.cleanup_session(),
-                    Err(error) => {
-                        error!("Couldn’t logout the session {}", error);
-                        let error = Error::new(
-                                clone!(@weak obj => @default-return None, move |_| {
-                                        let label = gtk::Label::new(Some(&gettext("Failed to logout the 
session.")));
-
-                                        Some(label.upcast())
-                                }),
-                        );
-
-                        if let Some(window) = obj.parent_window() {
-                            window.append_error(&error);
-                        }
-                    }
+        match handle.await.unwrap() {
+            Ok(_) => self.cleanup_session(),
+            Err(error) => {
+                error!("Couldn’t logout the session {}", error);
+                let error = Error::new(move |_| {
+                    let label = gtk::Label::new(Some(&gettext("Failed to logout the session.")));
+                    Some(label.upcast())
+                });
+
+                if let Some(window) = self.parent_window() {
+                    window.append_error(&error);
                 }
-            })
-        );
+            }
+        }
     }
 
     fn cleanup_session(&self) {
@@ -649,7 +748,22 @@ impl Session {
             error!("Failed to remove database after logout: {}", error);
         }
 
-        self.emit_by_name("logged-out", &[]).unwrap();
+        debug!("The logged out session was cleaned up");
+    }
+
+    /// Show the content of the session
+    pub fn show_content(&self) {
+        let priv_ = imp::Session::from_instance(self);
+
+        // FIXME: we should actually check if we have now the keys
+        priv_.stack.set_visible_child(&*priv_.content);
+        priv_.logout_on_dispose.set(false);
+
+        if let Some(session_verificiation) = priv_.stack.child_by_name("session-verification") {
+            priv_.stack.remove(&session_verificiation);
+        }
+
+        self.emit_by_name("ready", &[]).unwrap();
     }
 }
 
diff --git a/src/session/verification/emoji.rs b/src/session/verification/emoji.rs
new file mode 100644
index 00000000..4505c688
--- /dev/null
+++ b/src/session/verification/emoji.rs
@@ -0,0 +1,58 @@
+use adw::subclass::prelude::*;
+
+use gtk::{glib, prelude::*, subclass::prelude::*, CompositeTemplate};
+
+mod imp {
+    use super::*;
+    use glib::subclass::InitializingObject;
+
+    #[derive(Debug, Default, CompositeTemplate)]
+    #[template(resource = "/org/gnome/FractalNext/verification-emoji.ui")]
+    pub struct Emoji {
+        #[template_child]
+        pub emoji: TemplateChild<gtk::Label>,
+        #[template_child]
+        pub emoji_name: TemplateChild<gtk::Label>,
+    }
+
+    #[glib::object_subclass]
+    impl ObjectSubclass for Emoji {
+        const NAME: &'static str = "VerificationEmoji";
+        type Type = super::Emoji;
+        type ParentType = adw::Bin;
+
+        fn class_init(klass: &mut Self::Class) {
+            Self::bind_template(klass);
+        }
+
+        fn instance_init(obj: &InitializingObject<Self>) {
+            obj.init_template();
+        }
+    }
+
+    impl ObjectImpl for Emoji {}
+    impl WidgetImpl for Emoji {}
+    impl BinImpl for Emoji {}
+}
+
+glib::wrapper! {
+    /// Preference Window to display and update room details.
+    pub struct Emoji(ObjectSubclass<imp::Emoji>)
+        @extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
+}
+
+impl Emoji {
+    pub fn new(emoji: (&str, &str)) -> Self {
+        let obj: Self = glib::Object::new(&[]).expect("Failed to create Emoji");
+
+        obj.set_emoji(emoji);
+        obj
+    }
+
+    pub fn set_emoji(&self, emoji: (&str, &str)) {
+        let priv_ = imp::Emoji::from_instance(self);
+
+        priv_.emoji.set_text(emoji.0);
+        priv_.emoji_name.set_text(emoji.1);
+    }
+}
diff --git a/src/session/verification/identity_verification.rs 
b/src/session/verification/identity_verification.rs
new file mode 100644
index 00000000..a3da69e5
--- /dev/null
+++ b/src/session/verification/identity_verification.rs
@@ -0,0 +1,548 @@
+use crate::session::user::UserExt;
+use crate::session::User;
+use crate::spawn_tokio;
+use gtk::{glib, glib::clone, prelude::*, subclass::prelude::*};
+use log::error;
+use matrix_sdk::{
+    encryption::{
+        identities::RequestVerificationError,
+        verification::{
+            CancelInfo, QrVerification, SasVerification, Verification as MatrixVerification,
+            VerificationRequest,
+        },
+    },
+    ruma::{
+        api::client::r0::sync::sync_events::ToDevice, events::AnyToDeviceEvent, identifiers::UserId,
+    },
+    Client, Error as MatrixError,
+};
+use qrcode::QrCode;
+use tokio::sync::mpsc;
+
+#[derive(Debug, Eq, PartialEq, Clone, Copy)]
+pub enum State {
+    Request,
+    Ready,
+    Start,
+    Cancel,
+    Accept,
+    Key,
+    Mac,
+    Done,
+}
+
+impl Default for State {
+    fn default() -> Self {
+        Self::Request
+    }
+}
+
+#[derive(Debug, Clone)]
+pub enum Verification {
+    SasV1(SasVerification),
+    QrV1(QrVerification),
+    Request(VerificationRequest),
+}
+
+impl Verification {
+    fn cancel_info(&self) -> Option<CancelInfo> {
+        match self {
+            Verification::QrV1(verification) => verification.cancel_info(),
+            Verification::SasV1(verification) => verification.cancel_info(),
+            Verification::Request(verification) => verification.cancel_info(),
+        }
+    }
+}
+
+#[derive(Debug, Eq, PartialEq, Clone, Copy, glib::GEnum)]
+#[repr(u32)]
+#[genum(type_name = "VerificationMode")]
+pub enum Mode {
+    IdentityNotFound,
+    Unavailable,
+    Requested,
+    SasV1,
+    QrV1,
+    Completed,
+    Cancelled,
+}
+
+impl Default for Mode {
+    fn default() -> Self {
+        Self::Unavailable
+    }
+}
+
+#[derive(Debug, Eq, PartialEq, Clone, Copy)]
+pub enum UserAction {
+    Match,
+    NotMatch,
+    Cancel,
+    StartSas,
+}
+
+#[derive(Debug, Eq, PartialEq)]
+pub enum Message {
+    UserAction(UserAction),
+    Sync((String, State)),
+}
+
+mod imp {
+    use super::*;
+    use glib::object::WeakRef;
+    use glib::source::SourceId;
+    use once_cell::{sync::Lazy, unsync::OnceCell};
+    use std::cell::{Cell, RefCell};
+
+    #[derive(Debug, Default)]
+    pub struct IdentityVerification {
+        pub user: OnceCell<WeakRef<User>>,
+        pub mode: Cell<Mode>,
+        pub sync_sender: RefCell<Option<mpsc::Sender<Message>>>,
+        pub main_sender: OnceCell<glib::SyncSender<Verification>>,
+        pub request: RefCell<Option<Verification>>,
+        pub source_id: RefCell<Option<SourceId>>,
+    }
+
+    #[glib::object_subclass]
+    impl ObjectSubclass for IdentityVerification {
+        const NAME: &'static str = "IdentityVerification";
+        type Type = super::IdentityVerification;
+        type ParentType = glib::Object;
+    }
+
+    impl ObjectImpl for IdentityVerification {
+        fn properties() -> &'static [glib::ParamSpec] {
+            static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
+                vec![
+                    glib::ParamSpec::new_object(
+                        "user",
+                        "User",
+                        "The user to be verified",
+                        User::static_type(),
+                        glib::ParamFlags::READWRITE | glib::ParamFlags::CONSTRUCT_ONLY,
+                    ),
+                    glib::ParamSpec::new_enum(
+                        "mode",
+                        "Mode",
+                        "The verification mode used",
+                        Mode::static_type(),
+                        Mode::default() as i32,
+                        glib::ParamFlags::READABLE | glib::ParamFlags::EXPLICIT_NOTIFY,
+                    ),
+                ]
+            });
+
+            PROPERTIES.as_ref()
+        }
+
+        fn set_property(
+            &self,
+            obj: &Self::Type,
+            _id: usize,
+            value: &glib::Value,
+            pspec: &glib::ParamSpec,
+        ) {
+            match pspec.name() {
+                "user" => obj.set_user(value.get().unwrap()),
+                _ => unimplemented!(),
+            }
+        }
+
+        fn property(&self, obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
+            match pspec.name() {
+                "user" => obj.user().to_value(),
+                "mode" => obj.mode().to_value(),
+                _ => unimplemented!(),
+            }
+        }
+
+        fn constructed(&self, obj: &Self::Type) {
+            self.parent_constructed(obj);
+
+            let (main_sender, main_receiver) =
+                glib::MainContext::sync_channel::<Verification>(Default::default(), 100);
+
+            let source_id = main_receiver.attach(
+                None,
+                clone!(@weak obj => @default-return glib::Continue(false), move |verification| {
+                    let mode = match verification {
+                        Verification::QrV1(_) => Mode::QrV1,
+                        Verification::SasV1(_) => Mode::SasV1,
+                        Verification::Request(_) => Mode::Requested,
+                    };
+                    obj.set_request(Some(verification));
+                    obj.set_mode(mode);
+
+                    glib::Continue(true)
+                }),
+            );
+
+            self.main_sender.set(main_sender).unwrap();
+            self.source_id.replace(Some(source_id));
+        }
+
+        fn dispose(&self, obj: &Self::Type) {
+            obj.cancel();
+            if let Some(source_id) = self.source_id.take() {
+                let _ = glib::Source::remove(source_id);
+            }
+        }
+    }
+}
+
+glib::wrapper! {
+    pub struct IdentityVerification(ObjectSubclass<imp::IdentityVerification>);
+}
+
+impl IdentityVerification {
+    pub fn new(user: &User) -> Self {
+        glib::Object::new(&[("user", user)]).expect("Failed to create IdentityVerification")
+    }
+
+    pub fn user(&self) -> User {
+        let priv_ = imp::IdentityVerification::from_instance(self);
+        priv_.user.get().unwrap().upgrade().unwrap()
+    }
+
+    fn set_user(&self, user: User) {
+        let priv_ = imp::IdentityVerification::from_instance(self);
+        priv_.user.set(user.downgrade()).unwrap()
+    }
+
+    /// Start an interactive identity verification
+    /// Already in progress verifications are cancelled before starting a new one
+    pub async fn start(&self) -> Result<(), RequestVerificationError> {
+        let priv_ = imp::IdentityVerification::from_instance(self);
+        let user = self.user();
+        let client = user.session().client();
+        let user_id = user.user_id().clone();
+        let main_sender = priv_.main_sender.get().unwrap().clone();
+
+        self.set_request(None);
+        // TODO cancel any other request in progress
+
+        let (sync_sender, sync_receiver) = mpsc::channel(100);
+        priv_.sync_sender.replace(Some(sync_sender));
+
+        // TODO add timeout
+
+        let result =
+            spawn_tokio!(async move { start(client, user_id, main_sender, sync_receiver).await })
+                .await
+                .unwrap()?;
+
+        priv_.sync_sender.take();
+
+        self.set_mode(result);
+        Ok(())
+    }
+
+    pub fn emoji_match(&self) {
+        let priv_ = imp::IdentityVerification::from_instance(self);
+
+        if let Some(sync_sender) = &*priv_.sync_sender.borrow() {
+            let result = sync_sender.try_send(Message::UserAction(UserAction::Match));
+
+            if let Err(error) = result {
+                error!("Failed to send message to tokio runtime: {}", error);
+            }
+        }
+    }
+
+    pub fn emoji_not_match(&self) {
+        let priv_ = imp::IdentityVerification::from_instance(self);
+
+        if let Some(sync_sender) = &*priv_.sync_sender.borrow() {
+            let result = sync_sender.try_send(Message::UserAction(UserAction::NotMatch));
+            if let Err(error) = result {
+                error!("Failed to send message to tokio runtime: {}", error);
+            }
+        }
+    }
+
+    pub fn mode(&self) -> Mode {
+        let priv_ = imp::IdentityVerification::from_instance(self);
+        priv_.mode.get()
+    }
+
+    fn set_mode(&self, mode: Mode) {
+        let priv_ = imp::IdentityVerification::from_instance(self);
+
+        if self.mode() == mode {
+            return;
+        }
+
+        priv_.mode.set(mode);
+        self.notify("mode");
+    }
+
+    fn set_request(&self, request: Option<Verification>) {
+        let priv_ = imp::IdentityVerification::from_instance(self);
+
+        priv_.request.replace(request);
+    }
+
+    /// Get the QrCode for this verification request
+    ///
+    /// This is only set once the request reached the `State::Ready`
+    /// and if QrCode verification is possible
+    pub fn qr_code(&self) -> Option<QrCode> {
+        let priv_ = imp::IdentityVerification::from_instance(self);
+
+        match &*priv_.request.borrow() {
+            Some(Verification::QrV1(qr_verification)) => qr_verification.to_qr_code().ok(),
+            _ => None,
+        }
+    }
+
+    /// Get the Emojis for this verification request
+    ///
+    /// This is only set once the request reached the `State::Ready`
+    /// and if a Sas verification was started
+    pub fn emoji(&self) -> Option<[(&'static str, &'static str); 7]> {
+        let priv_ = imp::IdentityVerification::from_instance(self);
+
+        match &*priv_.request.borrow() {
+            Some(Verification::SasV1(qr_verification)) => qr_verification.emoji(),
+            _ => None,
+        }
+    }
+
+    pub fn start_sas(&self) {
+        let priv_ = imp::IdentityVerification::from_instance(self);
+
+        if let Some(sync_sender) = &*priv_.sync_sender.borrow() {
+            let result = sync_sender.try_send(Message::UserAction(UserAction::StartSas));
+
+            if let Err(error) = result {
+                error!("Failed to send message to tokio runtime: {}", error);
+            }
+        }
+    }
+
+    pub fn cancel(&self) {
+        let priv_ = imp::IdentityVerification::from_instance(self);
+        if let Some(sync_sender) = &*priv_.sync_sender.borrow() {
+            let result = sync_sender.try_send(Message::UserAction(UserAction::Cancel));
+            if let Err(error) = result {
+                error!("Failed to send message to tokio runtime: {}", error);
+            }
+        }
+    }
+
+    /// Get information about why the request was cancelled
+    pub fn cancel_info(&self) -> Option<CancelInfo> {
+        let priv_ = imp::IdentityVerification::from_instance(self);
+
+        if let Some(verification) = &*priv_.request.borrow() {
+            verification.cancel_info()
+        } else {
+            None
+        }
+    }
+
+    pub fn handle_response_to_device(&self, to_device: ToDevice) {
+        let priv_ = imp::IdentityVerification::from_instance(self);
+
+        for event in to_device.events.iter().filter_map(|e| e.deserialize().ok()) {
+            let (flow_id, state) = match event {
+                AnyToDeviceEvent::KeyVerificationRequest(e) => {
+                    (e.content.transaction_id, State::Request)
+                }
+                AnyToDeviceEvent::KeyVerificationReady(e) => {
+                    (e.content.transaction_id, State::Ready)
+                }
+                AnyToDeviceEvent::KeyVerificationStart(e) => {
+                    (e.content.transaction_id, State::Start)
+                }
+                AnyToDeviceEvent::KeyVerificationCancel(e) => {
+                    (e.content.transaction_id, State::Cancel)
+                }
+                AnyToDeviceEvent::KeyVerificationAccept(e) => {
+                    (e.content.transaction_id, State::Accept)
+                }
+                AnyToDeviceEvent::KeyVerificationMac(e) => (e.content.transaction_id, State::Mac),
+                AnyToDeviceEvent::KeyVerificationKey(e) => (e.content.transaction_id, State::Key),
+                AnyToDeviceEvent::KeyVerificationDone(e) => (e.content.transaction_id, State::Done),
+                _ => continue,
+            };
+
+            if let Some(sync_sender) = &*priv_.sync_sender.borrow() {
+                let result = sync_sender.try_send(Message::Sync((flow_id, state)));
+                if let Err(error) = result {
+                    error!("Failed to send message to tokio runtime: {}", error);
+                }
+            }
+        }
+    }
+}
+
+async fn start(
+    client: Client,
+    user_id: UserId,
+    main_sender: glib::SyncSender<Verification>,
+    mut sync_receiver: mpsc::Receiver<Message>,
+) -> Result<Mode, RequestVerificationError> {
+    let identity = if let Some(identity) = client
+        .get_user_identity(&user_id)
+        .await
+        .map_err(|error| RequestVerificationError::Sdk(MatrixError::CryptoStoreError(error)))?
+    {
+        identity
+    } else {
+        return Ok(Mode::IdentityNotFound);
+    };
+
+    let request = identity.request_verification().await?;
+    let flow_id = request.flow_id();
+
+    let result = main_sender.send(Verification::Request(request.clone()));
+
+    if let Err(error) = result {
+        error!("Failed to send message to the main context: {}", error);
+    }
+
+    if wait_for_state(flow_id, State::Ready, &mut sync_receiver).await {
+        request.cancel().await?;
+        return Ok(Mode::Cancelled);
+    }
+
+    let qr_verification = request
+        .generate_qr_code()
+        .await
+        .map_err(|error| RequestVerificationError::Sdk(error))?;
+
+    let start_sas = if let Some(qr_verification) = qr_verification {
+        let result = main_sender.send(Verification::QrV1(qr_verification));
+
+        if let Err(error) = result {
+            error!("Failed to send message to the main context: {}", error);
+        }
+
+        let (start_sas, cancel) = loop {
+            match sync_receiver.recv().await.unwrap() {
+                Message::Sync((id, State::Start)) if flow_id == &id => break (false, false),
+                Message::Sync((id, State::Cancel)) if flow_id == &id => break (false, true),
+                Message::UserAction(UserAction::Cancel) => break (false, true),
+                Message::UserAction(UserAction::StartSas) => break (true, false),
+                _ => {}
+            }
+        };
+
+        if cancel {
+            request.cancel().await?;
+            return Ok(Mode::Cancelled);
+        }
+        start_sas
+    } else {
+        true
+    };
+
+    if start_sas {
+        if request
+            .start_sas()
+            .await
+            .map_err(|error| RequestVerificationError::Sdk(error))?
+            .is_some()
+        {
+            let cancel = loop {
+                match sync_receiver.recv().await {
+                    Some(Message::Sync((id, State::Start))) if flow_id == &id => break false,
+                    Some(Message::Sync((id, State::Accept))) if flow_id == &id => break false,
+                    Some(Message::Sync((id, State::Cancel))) if flow_id == &id => break true,
+                    Some(Message::UserAction(UserAction::Cancel)) => break true,
+                    None => break true,
+                    _ => {}
+                }
+            };
+
+            if cancel {
+                request.cancel().await?;
+                return Ok(Mode::Cancelled);
+            }
+        } else {
+            return Ok(Mode::Unavailable);
+        }
+    }
+
+    // Get the verification struct from the sdk, this way we are sure we get the correct type
+    let verification = if let Some(verification) = client.get_verification(&user_id, &flow_id).await
+    {
+        verification
+    } else {
+        return Ok(Mode::Unavailable);
+    };
+
+    match verification {
+        MatrixVerification::QrV1(qr_verification) => {
+            qr_verification.confirm().await?;
+
+            if wait_for_state(flow_id, State::Done, &mut sync_receiver).await {
+                request.cancel().await?;
+                return Ok(Mode::Cancelled);
+            }
+        }
+        MatrixVerification::SasV1(sas_verification) => {
+            sas_verification.accept().await?;
+
+            if wait_for_state(flow_id, State::Key, &mut sync_receiver).await {
+                request.cancel().await?;
+                return Ok(Mode::Cancelled);
+            }
+
+            let result = main_sender.send(Verification::SasV1(sas_verification.clone()));
+
+            if let Err(error) = result {
+                error!("Failed to send message to the main context: {}", error);
+            }
+
+            if wait_for_match_action(flow_id, &mut sync_receiver).await {
+                request.cancel().await?;
+                return Ok(Mode::Cancelled);
+            }
+
+            sas_verification.confirm().await?;
+
+            if wait_for_state(flow_id, State::Done, &mut sync_receiver).await {
+                request.cancel().await?;
+                return Ok(Mode::Cancelled);
+            }
+        }
+    }
+
+    Ok(Mode::Completed)
+}
+
+async fn wait_for_state(
+    flow_id: &str,
+    expected_state: State,
+    sync_receiver: &mut mpsc::Receiver<Message>,
+) -> bool {
+    loop {
+        match sync_receiver.recv().await {
+            Some(Message::Sync((id, State::Cancel))) if flow_id == &id => return true,
+            Some(Message::Sync((id, state))) if flow_id == &id && expected_state == state => break,
+            Some(Message::UserAction(UserAction::Cancel)) => return true,
+            None => return true,
+            _ => {}
+        }
+    }
+
+    false
+}
+
+async fn wait_for_match_action(flow_id: &str, sync_receiver: &mut mpsc::Receiver<Message>) -> bool {
+    loop {
+        match sync_receiver.recv().await {
+            Some(Message::Sync((id, State::Cancel))) if flow_id == &id => return true,
+            Some(Message::UserAction(UserAction::Match)) => break,
+            Some(Message::UserAction(UserAction::NotMatch)) => return true,
+            Some(Message::UserAction(UserAction::Cancel)) => return true,
+            None => return true,
+            _ => {}
+        }
+    }
+
+    false
+}
diff --git a/src/session/verification/mod.rs b/src/session/verification/mod.rs
new file mode 100644
index 00000000..ba2e3074
--- /dev/null
+++ b/src/session/verification/mod.rs
@@ -0,0 +1,9 @@
+mod emoji;
+mod identity_verification;
+mod session_verification;
+mod to_device_handler;
+
+pub use self::emoji::Emoji;
+pub use self::identity_verification::{IdentityVerification, Mode as VerificationMode};
+pub use self::session_verification::SessionVerification;
+pub use self::to_device_handler::ToDeviceHandler;
diff --git a/src/session/verification/session_verification.rs 
b/src/session/verification/session_verification.rs
new file mode 100644
index 00000000..88986c62
--- /dev/null
+++ b/src/session/verification/session_verification.rs
@@ -0,0 +1,424 @@
+use adw::subclass::prelude::*;
+use gettextrs::gettext;
+use gtk::{glib, glib::clone, prelude::*, subclass::prelude::*, CompositeTemplate};
+use log::{debug, error, warn};
+
+use crate::components::{AuthDialog, SpinnerButton};
+use crate::contrib::QRCode;
+use crate::contrib::QRCodeExt;
+use crate::session::verification::{Emoji, IdentityVerification, VerificationMode};
+use crate::session::Session;
+use crate::spawn;
+use crate::Error;
+use crate::Window;
+use matrix_sdk::ruma::events::key::verification::cancel::CancelCode;
+
+mod imp {
+    use super::*;
+    use glib::object::WeakRef;
+    use glib::subclass::InitializingObject;
+    use glib::SignalHandlerId;
+    use once_cell::unsync::OnceCell;
+    use std::cell::RefCell;
+
+    #[derive(Debug, Default, CompositeTemplate)]
+    #[template(resource = "/org/gnome/FractalNext/session-verification.ui")]
+    pub struct SessionVerification {
+        pub request: OnceCell<WeakRef<IdentityVerification>>,
+        pub session: OnceCell<WeakRef<Session>>,
+        #[template_child]
+        pub bootstrap_button: TemplateChild<SpinnerButton>,
+        #[template_child]
+        pub qrcode: TemplateChild<QRCode>,
+        #[template_child]
+        pub emoji_row_1: TemplateChild<gtk::Box>,
+        #[template_child]
+        pub emoji_row_2: TemplateChild<gtk::Box>,
+        #[template_child]
+        pub emoji_match_btn: TemplateChild<SpinnerButton>,
+        #[template_child]
+        pub emoji_not_match_btn: TemplateChild<SpinnerButton>,
+        #[template_child]
+        pub start_emoji_btn: TemplateChild<SpinnerButton>,
+        #[template_child]
+        pub main_stack: TemplateChild<gtk::Stack>,
+        pub mode_handler: RefCell<Option<SignalHandlerId>>,
+    }
+
+    #[glib::object_subclass]
+    impl ObjectSubclass for SessionVerification {
+        const NAME: &'static str = "SessionVerification";
+        type Type = super::SessionVerification;
+        type ParentType = adw::Bin;
+
+        fn class_init(klass: &mut Self::Class) {
+            SpinnerButton::static_type();
+            QRCode::static_type();
+            Emoji::static_type();
+            Self::bind_template(klass);
+
+            klass.install_action("verification.show-recovery", None, move |obj, _, _| {
+                obj.show_recovery();
+            });
+
+            klass.install_action("verification.previous", None, move |obj, _, _| {
+                obj.previous();
+            });
+        }
+
+        fn instance_init(obj: &InitializingObject<Self>) {
+            obj.init_template();
+        }
+    }
+
+    impl ObjectImpl for SessionVerification {
+        fn properties() -> &'static [glib::ParamSpec] {
+            use once_cell::sync::Lazy;
+            static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
+                vec![
+                    glib::ParamSpec::new_object(
+                        "request",
+                        "Request",
+                        "The Object holding the data for the verification",
+                        IdentityVerification::static_type(),
+                        glib::ParamFlags::READWRITE | glib::ParamFlags::CONSTRUCT_ONLY,
+                    ),
+                    glib::ParamSpec::new_object(
+                        "session",
+                        "Session",
+                        "The current Session",
+                        Session::static_type(),
+                        glib::ParamFlags::READWRITE | glib::ParamFlags::CONSTRUCT_ONLY,
+                    ),
+                ]
+            });
+
+            PROPERTIES.as_ref()
+        }
+
+        fn set_property(
+            &self,
+            obj: &Self::Type,
+            _id: usize,
+            value: &glib::Value,
+            pspec: &glib::ParamSpec,
+        ) {
+            match pspec.name() {
+                "request" => obj.set_request(value.get().unwrap()),
+                "session" => obj.set_session(value.get().unwrap()),
+                _ => unimplemented!(),
+            }
+        }
+
+        fn property(&self, obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
+            match pspec.name() {
+                "request" => obj.request().to_value(),
+                "session" => obj.session().to_value(),
+                _ => unimplemented!(),
+            }
+        }
+
+        fn constructed(&self, obj: &Self::Type) {
+            self.parent_constructed(obj);
+
+            obj.action_set_enabled("verification.show-recovery", false);
+
+            self.emoji_match_btn
+                .connect_clicked(clone!(@weak obj => move |button| {
+                    let priv_ = imp::SessionVerification::from_instance(&obj);
+                    button.set_loading(true);
+                    priv_.emoji_not_match_btn.set_sensitive(false);
+                    obj.request().emoji_match();
+                }));
+
+            self.emoji_not_match_btn
+                .connect_clicked(clone!(@weak obj => move |button| {
+                    let priv_ = imp::SessionVerification::from_instance(&obj);
+                    button.set_loading(true);
+                    priv_.emoji_match_btn.set_sensitive(false);
+                    obj.request().emoji_not_match();
+                }));
+
+            self.start_emoji_btn
+                .connect_clicked(clone!(@weak obj => move |button| {
+                    button.set_loading(true);
+                    obj.request().start_sas();
+                }));
+
+            self.bootstrap_button
+                .connect_clicked(clone!(@weak obj => move |button| {
+                button.set_loading(true);
+                obj.bootstrap_cross_signing();
+                }));
+        }
+
+        fn dispose(&self, obj: &Self::Type) {
+            obj.silent_cancel();
+        }
+    }
+
+    impl WidgetImpl for SessionVerification {}
+    impl BinImpl for SessionVerification {}
+}
+
+glib::wrapper! {
+    pub struct SessionVerification(ObjectSubclass<imp::SessionVerification>)
+        @extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
+}
+
+impl SessionVerification {
+    pub fn new(request: &IdentityVerification, session: &Session) -> Self {
+        glib::Object::new(&[("request", request), ("session", session)])
+            .expect("Failed to create SessionVerification")
+    }
+
+    pub fn request(&self) -> IdentityVerification {
+        let priv_ = imp::SessionVerification::from_instance(self);
+        priv_.request.get().unwrap().upgrade().unwrap()
+    }
+
+    fn set_request(&self, request: IdentityVerification) {
+        let priv_ = imp::SessionVerification::from_instance(self);
+
+        priv_.request.set(request.downgrade()).unwrap()
+    }
+
+    pub fn session(&self) -> Session {
+        let priv_ = imp::SessionVerification::from_instance(self);
+        priv_.session.get().unwrap().upgrade().unwrap()
+    }
+
+    fn set_session(&self, session: Session) {
+        let priv_ = imp::SessionVerification::from_instance(self);
+
+        priv_.session.set(session.downgrade()).unwrap()
+    }
+
+    /// Returns the parent GtkWindow containing this widget.
+    fn parent_window(&self) -> Option<Window> {
+        self.root()?.downcast().ok()
+    }
+
+    fn reset(&self) {
+        let priv_ = imp::SessionVerification::from_instance(self);
+        priv_.emoji_not_match_btn.set_loading(false);
+        priv_.emoji_not_match_btn.set_sensitive(true);
+        priv_.emoji_match_btn.set_loading(false);
+        priv_.emoji_match_btn.set_sensitive(true);
+        priv_.start_emoji_btn.set_loading(false);
+        priv_.start_emoji_btn.set_sensitive(true);
+        priv_.bootstrap_button.set_loading(false);
+
+        while let Some(child) = priv_.emoji_row_1.first_child() {
+            priv_.emoji_row_1.remove(&child);
+        }
+
+        while let Some(child) = priv_.emoji_row_2.first_child() {
+            priv_.emoji_row_2.remove(&child);
+        }
+    }
+
+    pub fn start_verification(&self) {
+        let priv_ = imp::SessionVerification::from_instance(self);
+        let request = self.request();
+
+        self.reset();
+
+        let handler = request.connect_notify_local(
+            Some("mode"),
+            clone!(@weak self as obj => move |_, _| {
+                obj.update_view();
+            }),
+        );
+
+        priv_.mode_handler.replace(Some(handler));
+
+        let weak_obj = self.downgrade();
+        spawn!(clone!(@weak request => async move {
+            if let Err(error) = request.start().await {
+                if let Some(obj) =  weak_obj.upgrade() {
+                    obj.show_error();
+                }
+                error!("Verification failed: {}", error);
+            }
+        }));
+    }
+
+    /// Cancel the verification request without telling the user about it
+    fn silent_cancel(&self) {
+        let priv_ = imp::SessionVerification::from_instance(self);
+
+        if let Some(handler) = priv_.mode_handler.take() {
+            self.request().disconnect(handler);
+        }
+
+        debug!("Verification request was silently canceled");
+
+        self.request().cancel();
+    }
+
+    fn update_view(&self) {
+        let priv_ = imp::SessionVerification::from_instance(self);
+        let request = self.request();
+        match request.mode() {
+            VerificationMode::IdentityNotFound => {
+                priv_.main_stack.set_visible_child_name("bootstrap");
+            }
+            VerificationMode::Requested => {
+                priv_.main_stack.set_visible_child_name("wait-for-device");
+            }
+            VerificationMode::QrV1 => {
+                if let Some(qrcode) = request.qr_code() {
+                    priv_.qrcode.set_qrcode(qrcode);
+                    priv_.main_stack.set_visible_child_name("qrcode");
+                } else {
+                    warn!("Failed to get qrcode for QrVerification");
+                    request.start_sas();
+                }
+            }
+            VerificationMode::SasV1 => {
+                // TODO: implement sas fallback when emojis arn't supported
+                if let Some(emoji) = request.emoji() {
+                    for (index, emoji) in emoji.iter().enumerate() {
+                        if index < 4 {
+                            priv_.emoji_row_1.append(&Emoji::new(*emoji));
+                        } else {
+                            priv_.emoji_row_2.append(&Emoji::new(*emoji));
+                        }
+                    }
+                    priv_.main_stack.set_visible_child_name("emoji");
+                } else {
+                    warn!("Failed to get emoji for SasVerification");
+                    self.show_error();
+                }
+            }
+            VerificationMode::Unavailable => {
+                self.show_error();
+            }
+            VerificationMode::Completed => {
+                priv_.main_stack.set_visible_child_name("completed");
+            }
+            VerificationMode::Cancelled => {
+                self.show_error();
+            }
+        }
+    }
+
+    fn show_error(&self) {
+        let error_message = if let Some(info) = self.request().cancel_info() {
+            match info.cancel_code() {
+                CancelCode::User => Some(gettext("You cancelled the verificaiton process.")),
+                CancelCode::Timeout => Some(gettext(
+                    "The verification process failed because it reached a timeout.",
+                )),
+                _ => match info.cancel_code().as_str() {
+                    "m.mismatched_sas" => Some(gettext("The emoji did not match.")),
+                    _ => None,
+                },
+            }
+        } else {
+            None
+        };
+
+        let error_message = error_message.unwrap_or_else(|| {
+            gettext("An unknown error occured during the verification process.")
+        });
+
+        let error = Error::new(move |_| {
+            let error_label = gtk::LabelBuilder::new()
+                .label(&error_message)
+                .wrap(true)
+                .build();
+            Some(error_label.upcast())
+        });
+
+        if let Some(window) = self.session().parent_window() {
+            window.append_error(&error);
+        }
+        self.silent_cancel();
+        self.start_verification();
+    }
+
+    fn show_recovery(&self) {
+        let priv_ = imp::SessionVerification::from_instance(self);
+
+        self.silent_cancel();
+
+        priv_.main_stack.set_visible_child_name("recovery");
+    }
+
+    fn previous(&self) {
+        let priv_ = imp::SessionVerification::from_instance(self);
+
+        match priv_.main_stack.visible_child_name().unwrap().as_str() {
+            "recovery" => {
+                self.silent_cancel();
+                self.start_verification();
+            }
+            "recovery-passphrase" | "recovery-key" => {
+                priv_.main_stack.set_visible_child_name("recovery");
+            }
+            "wait-for-device" | "complete" => {
+                self.silent_cancel();
+                self.activate_action("session.logout", None);
+            }
+            "emoji" | "qrcode" => {
+                self.silent_cancel();
+                self.start_verification();
+            }
+            _ => {}
+        }
+    }
+
+    fn bootstrap_cross_signing(&self) {
+        spawn!(clone!(@weak self as obj => async move {
+            let priv_ = imp::SessionVerification::from_instance(&obj);
+            let dialog = AuthDialog::new(obj.parent_window().as_ref(), &obj.session());
+
+            let result = dialog
+            .authenticate(move |client, auth_data| async move {
+                if let Some(auth) = auth_data {
+                    let auth = Some(auth.as_matrix_auth_data());
+                    client.bootstrap_cross_signing(auth).await
+                } else {
+                    client.bootstrap_cross_signing(None).await
+                }
+            })
+            .await;
+
+
+            let error_message = match result {
+                Some(Ok(_)) => None,
+                Some(Err(error)) => {
+                    error!("Failed to bootstap cross singing: {}", error);
+                    Some(gettext("An error occured during the creation of the encryption keys."))
+                },
+                None => {
+                    error!("Failed to bootstap cross singing: User cancelled the authentication");
+                    Some(gettext("You cancelled the authentication needed to create the encryption keys."))
+                },
+            };
+
+            if let Some(error_message) = error_message {
+                let error = Error::new(move |_| {
+                    let error_label = gtk::LabelBuilder::new()
+                        .label(&error_message)
+                        .wrap(true)
+                        .build();
+                    Some(error_label.upcast())
+                });
+
+                if let Some(window) = obj.session().parent_window() {
+                    window.append_error(&error);
+                }
+
+                obj.silent_cancel();
+                obj.start_verification();
+            } else {
+                priv_
+                .main_stack
+                .set_visible_child_name("completed");
+            }
+        }));
+    }
+}
diff --git a/src/session/verification/to_device_handler.rs b/src/session/verification/to_device_handler.rs
new file mode 100644
index 00000000..ba330844
--- /dev/null
+++ b/src/session/verification/to_device_handler.rs
@@ -0,0 +1,95 @@
+use crate::session::{verification::IdentityVerification, Session};
+use gtk::{glib, prelude::*, subclass::prelude::*};
+use matrix_sdk::ruma::api::client::r0::sync::sync_events::ToDevice;
+
+mod imp {
+    use super::*;
+    use glib::object::WeakRef;
+    use once_cell::sync::{Lazy, OnceCell};
+    use std::cell::RefCell;
+
+    #[derive(Debug, Default)]
+    pub struct ToDeviceHandler {
+        pub session: OnceCell<WeakRef<Session>>,
+        pub verifications: RefCell<Vec<IdentityVerification>>,
+    }
+
+    #[glib::object_subclass]
+    impl ObjectSubclass for ToDeviceHandler {
+        const NAME: &'static str = "ToDeviceHandler";
+        type Type = super::ToDeviceHandler;
+        type ParentType = glib::Object;
+    }
+
+    impl ObjectImpl for ToDeviceHandler {
+        fn properties() -> &'static [glib::ParamSpec] {
+            static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
+                vec![glib::ParamSpec::new_object(
+                    "session",
+                    "Session",
+                    "The session",
+                    Session::static_type(),
+                    glib::ParamFlags::READWRITE | glib::ParamFlags::CONSTRUCT_ONLY,
+                )]
+            });
+
+            PROPERTIES.as_ref()
+        }
+
+        fn set_property(
+            &self,
+            obj: &Self::Type,
+            _id: usize,
+            value: &glib::Value,
+            pspec: &glib::ParamSpec,
+        ) {
+            match pspec.name() {
+                "session" => obj.set_session(value.get().unwrap()),
+                _ => unimplemented!(),
+            }
+        }
+
+        fn property(&self, obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
+            match pspec.name() {
+                "session" => obj.session().to_value(),
+                _ => unimplemented!(),
+            }
+        }
+    }
+}
+
+glib::wrapper! {
+    pub struct ToDeviceHandler(ObjectSubclass<imp::ToDeviceHandler>);
+}
+
+impl ToDeviceHandler {
+    pub fn new(session: &Session) -> Self {
+        glib::Object::new(&[("session", session)]).expect("Failed to create ToDeviceHandler")
+    }
+
+    pub fn session(&self) -> Session {
+        let priv_ = imp::ToDeviceHandler::from_instance(self);
+        priv_.session.get().unwrap().upgrade().unwrap()
+    }
+
+    fn set_session(&self, session: Session) {
+        let priv_ = imp::ToDeviceHandler::from_instance(self);
+        priv_.session.set(session.downgrade()).unwrap()
+    }
+
+    pub fn handle_response_to_device(&self, to_device: ToDevice) {
+        let priv_ = imp::ToDeviceHandler::from_instance(self);
+
+        for verification in &*priv_.verifications.borrow() {
+            // TODO: handle incomming requests
+            verification.handle_response_to_device(to_device.clone());
+        }
+    }
+
+    /// Add a new `IdentityVerification` request that should be tracked
+    pub fn add_request(&self, request: IdentityVerification) {
+        let priv_ = imp::ToDeviceHandler::from_instance(self);
+
+        priv_.verifications.borrow_mut().push(request);
+    }
+}
diff --git a/src/window.rs b/src/window.rs
index af118dd9..6a07ad27 100644
--- a/src/window.rs
+++ b/src/window.rs
@@ -123,7 +123,7 @@ impl Window {
         if let Some(child) = priv_.sessions.first_child() {
             priv_.sessions.set_visible_child(&child);
         } else {
-            self.switch_to_login_page();
+            self.switch_to_login_page(true);
         }
     }
 
@@ -181,13 +181,15 @@ impl Window {
         priv_.main_stack.set_visible_child(&priv_.sessions.get());
     }
 
-    pub fn switch_to_login_page(&self) {
+    pub fn switch_to_login_page(&self, clean: bool) {
         let priv_ = imp::Window::from_instance(self);
+        if clean {
+            priv_.login.clean();
+        }
         priv_
             .login
-            .get()
             .show_back_to_session_button(priv_.sessions.get().pages().n_items() > 0);
-        priv_.main_stack.set_visible_child(&priv_.login.get());
+        priv_.main_stack.set_visible_child(&*priv_.login);
     }
 
     /// This appends a new error to the list of errors


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]