Updating UI elements
This commit is contained in:
parent
b5fb91bcfd
commit
b976d0957a
171
css/main.css
171
css/main.css
@ -113,14 +113,6 @@ a:focus-visible {
|
||||
box-shadow: 0 0 0 0.1rem rgba(var(--text-color), 1) inset;
|
||||
}
|
||||
|
||||
a.button {
|
||||
padding: 0.4rem 0.6rem;
|
||||
border-radius: 0.3rem;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
button,
|
||||
.button {
|
||||
-webkit-user-select: none;
|
||||
@ -134,7 +126,7 @@ button,
|
||||
color: inherit;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
align-items: center;
|
||||
font-size: 0.9rem;
|
||||
font-size: inherit;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
padding: 0.8rem;
|
||||
@ -151,27 +143,45 @@ button:not(:disabled),
|
||||
}
|
||||
|
||||
.button {
|
||||
background-color: rgba(var(--text-color), 0.1);
|
||||
}
|
||||
.button--primary, .button--danger {
|
||||
color: rgba(var(--background-color), 1) !important;
|
||||
}
|
||||
.button--primary .icon, .button--danger .icon {
|
||||
fill: rgba(var(--background-color), 1);
|
||||
background-color: rgba(var(--text-color), 0.02);
|
||||
border: solid thin rgba(var(--text-color), 0.06);
|
||||
}
|
||||
.button--primary {
|
||||
color: rgba(var(--background-color), 1);
|
||||
background-color: var(--accent-color);
|
||||
}
|
||||
.button--primary .icon {
|
||||
fill: rgba(var(--background-color), 1);
|
||||
}
|
||||
.button--colored {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
.button--colored .icon {
|
||||
fill: var(--accent-color);
|
||||
}
|
||||
.button--danger {
|
||||
background-color: var(--danger-color);
|
||||
background-color: rgba(255, 115, 115, 0.062745098);
|
||||
color: var(--danger-color);
|
||||
}
|
||||
.button--danger .icon {
|
||||
fill: var(--danger-color);
|
||||
}
|
||||
.button--small {
|
||||
padding: 0.4rem 0.6rem;
|
||||
}
|
||||
.button--outlined {
|
||||
border: solid rgba(var(--text-color), 0.5) 0.1rem;
|
||||
border: solid rgba(var(--text-color), 0.3) 0.1rem;
|
||||
background-color: rgba(var(--foreground-color), 1);
|
||||
}
|
||||
.button--transparent {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
filter: saturate(0);
|
||||
}
|
||||
|
||||
.cta {
|
||||
text-transform: uppercase;
|
||||
@ -195,10 +205,6 @@ button:not(:disabled),
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
a:-webkit-any-link:focus-visible {
|
||||
outline: rgba(var(--text-color), 1) 0.1rem solid;
|
||||
}
|
||||
@ -260,20 +266,6 @@ sm-textarea {
|
||||
--max-height: 32ch;
|
||||
}
|
||||
|
||||
sm-button {
|
||||
--padding: 0.8rem;
|
||||
}
|
||||
sm-button[variant=primary] .icon {
|
||||
fill: rgba(var(--background-color), 1);
|
||||
}
|
||||
sm-button[disabled] .icon {
|
||||
fill: rgba(var(--text-color), 0.6);
|
||||
}
|
||||
sm-button.danger {
|
||||
--background: var(--danger-color);
|
||||
color: rgba(var(--background-color), 1);
|
||||
}
|
||||
|
||||
sm-spinner {
|
||||
--size: 1rem;
|
||||
--stroke-width: 0.1rem;
|
||||
@ -292,14 +284,14 @@ sm-option {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
strip-select {
|
||||
sm-chips {
|
||||
--gap: 0;
|
||||
background-color: rgba(var(--text-color), 0.06);
|
||||
border-radius: 0.3rem;
|
||||
padding: 0.3rem;
|
||||
}
|
||||
|
||||
strip-option {
|
||||
sm-chip {
|
||||
position: relative;
|
||||
font-size: 0.8rem;
|
||||
--border-radius: 0.2rem;
|
||||
@ -632,6 +624,7 @@ ol li::before {
|
||||
display: grid;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
}
|
||||
.multi-state-button > * {
|
||||
grid-area: 1/1/2/2;
|
||||
@ -640,6 +633,17 @@ ol li::before {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.password-field label {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.password-field label input:checked ~ .visible {
|
||||
display: none;
|
||||
}
|
||||
.password-field label input:not(:checked) ~ .invisible {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.page {
|
||||
height: 100%;
|
||||
}
|
||||
@ -679,38 +683,35 @@ ol li::before {
|
||||
}
|
||||
#confirmation_popup h4,
|
||||
#prompt_popup h4 {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
#confirmation_popup sm-button,
|
||||
#prompt_popup sm-button {
|
||||
margin: 0;
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
#confirmation_popup .flex,
|
||||
#prompt_popup .flex {
|
||||
padding: 0;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
#confirmation_popup .flex sm-button:first-of-type,
|
||||
#prompt_popup .flex sm-button:first-of-type {
|
||||
margin-right: 0.6rem;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
#prompt_message {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.popup__header {
|
||||
position: relative;
|
||||
display: grid;
|
||||
gap: 0.5rem;
|
||||
width: 100%;
|
||||
padding: 0 1.5rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.popup__header > * {
|
||||
grid-row: 1;
|
||||
}
|
||||
.popup__header h3,
|
||||
.popup__header h4 {
|
||||
grid-column: 1/-1;
|
||||
justify-self: center;
|
||||
align-self: center;
|
||||
}
|
||||
.popup__header__close {
|
||||
padding: 0.5rem;
|
||||
margin-left: -0.5rem;
|
||||
grid-column: 1;
|
||||
margin-left: -1rem;
|
||||
justify-self: flex-start;
|
||||
}
|
||||
|
||||
.flo-icon {
|
||||
@ -733,7 +734,13 @@ ol li::before {
|
||||
}
|
||||
|
||||
.inner-page {
|
||||
background-color: rgba(var(--foreground-color), 0.5);
|
||||
gap: 1rem;
|
||||
display: grid;
|
||||
position: relative;
|
||||
padding: 1rem;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
height: 100%;
|
||||
background-color: rgba(var(--foreground-color), 0.3);
|
||||
}
|
||||
|
||||
#sign_in,
|
||||
@ -744,7 +751,7 @@ ol li::before {
|
||||
#sign_in section,
|
||||
#sign_up section {
|
||||
margin-top: -8rem;
|
||||
width: min(24rem, 100%);
|
||||
width: min(26rem, 100%);
|
||||
}
|
||||
#sign_in sm-form,
|
||||
#sign_up sm-form {
|
||||
@ -754,25 +761,24 @@ ol li::before {
|
||||
#sign_up .h2 {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
#sign_up .card {
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
#sign_up h5 {
|
||||
color: rgba(var(--text-color), 0.8);
|
||||
|
||||
.generated-keys-wrapper {
|
||||
padding: 1rem;
|
||||
background-color: rgba(var(--foreground-color), 1);
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
#flo_id_warning {
|
||||
padding-bottom: 1.5rem;
|
||||
border-bottom: thin solid rgba(var(--text-color), 0.3);
|
||||
}
|
||||
#flo_id_warning .icon {
|
||||
height: 4rem;
|
||||
width: 4rem;
|
||||
padding: 1rem;
|
||||
height: 3rem;
|
||||
width: 3rem;
|
||||
padding: 0.8rem;
|
||||
overflow: visible;
|
||||
background-color: #ffc107;
|
||||
border-radius: 3rem;
|
||||
fill: rgba(0, 0, 0, 0.8);
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.tip {
|
||||
@ -780,10 +786,6 @@ ol li::before {
|
||||
color: rgba(var(--text-color), 0.8);
|
||||
}
|
||||
|
||||
sm-button[variant=primary] {
|
||||
--foreground-color: 255, 255, 255;
|
||||
}
|
||||
|
||||
.danger {
|
||||
color: var(--error-color);
|
||||
}
|
||||
@ -1666,10 +1668,10 @@ sm-button[variant=primary] {
|
||||
width: -moz-fit-content;
|
||||
width: fit-content;
|
||||
}
|
||||
#mail_type_selector strip-option {
|
||||
#mail_type_selector sm-chip {
|
||||
--padding: 0.5rem 1.2rem;
|
||||
}
|
||||
#mail_type_selector strip-option .badge {
|
||||
#mail_type_selector sm-chip .badge {
|
||||
margin: 0rem;
|
||||
}
|
||||
|
||||
@ -2064,7 +2066,7 @@ sm-button[variant=primary] {
|
||||
padding: 0 1.5rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
#mail > .flex sm-button:first-of-type {
|
||||
#mail > .flex button:first-of-type {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
@ -2217,26 +2219,9 @@ sm-button[variant=primary] {
|
||||
font-size: 2rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
#landing sm-button {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
width: 100% !important;
|
||||
}
|
||||
#landing_illustration {
|
||||
grid-area: illustration;
|
||||
}
|
||||
.frame form,
|
||||
#sign_in form {
|
||||
height: 100%;
|
||||
}
|
||||
.frame .h2,
|
||||
#sign_in .h2 {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.frame sm-button[variant=primary],
|
||||
#sign_in sm-button[variant=primary] {
|
||||
margin-top: auto;
|
||||
}
|
||||
.fab {
|
||||
bottom: 4rem;
|
||||
}
|
||||
@ -2348,6 +2333,8 @@ sm-button[variant=primary] {
|
||||
}
|
||||
#sign_in {
|
||||
width: 24rem;
|
||||
height: auto;
|
||||
border-radius: 0.4rem;
|
||||
}
|
||||
#main_page {
|
||||
grid-template-columns: -webkit-min-content 1fr;
|
||||
|
||||
2
css/main.min.css
vendored
2
css/main.min.css
vendored
File diff suppressed because one or more lines are too long
193
css/main.scss
193
css/main.scss
@ -124,14 +124,6 @@ a {
|
||||
}
|
||||
}
|
||||
|
||||
a.button {
|
||||
padding: 0.4rem 0.6rem;
|
||||
border-radius: 0.3rem;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
button,
|
||||
.button {
|
||||
user-select: none;
|
||||
@ -143,13 +135,12 @@ button,
|
||||
color: inherit;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
align-items: center;
|
||||
font-size: 0.9rem;
|
||||
font-size: inherit;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
padding: 0.8rem;
|
||||
border-radius: 0.3rem;
|
||||
justify-content: center;
|
||||
|
||||
&:focus-visible {
|
||||
outline: var(--accent-color) solid medium;
|
||||
}
|
||||
@ -160,23 +151,28 @@ button,
|
||||
}
|
||||
|
||||
.button {
|
||||
background-color: rgba(var(--text-color), 0.1);
|
||||
|
||||
&--primary,
|
||||
&--danger {
|
||||
color: rgba(var(--background-color), 1) !important;
|
||||
background-color: rgba(var(--text-color), 0.02);
|
||||
border: solid thin rgba(var(--text-color), 0.06);
|
||||
&--primary {
|
||||
color: rgba(var(--background-color), 1);
|
||||
background-color: var(--accent-color);
|
||||
|
||||
.icon {
|
||||
fill: rgba(var(--background-color), 1);
|
||||
}
|
||||
}
|
||||
|
||||
&--primary {
|
||||
background-color: var(--accent-color);
|
||||
&--colored {
|
||||
color: var(--accent-color);
|
||||
.icon {
|
||||
fill: var(--accent-color);
|
||||
}
|
||||
}
|
||||
|
||||
&--danger {
|
||||
background-color: var(--danger-color);
|
||||
background-color: #ff737310;
|
||||
color: var(--danger-color);
|
||||
.icon {
|
||||
fill: var(--danger-color);
|
||||
}
|
||||
}
|
||||
|
||||
&--small {
|
||||
@ -184,9 +180,17 @@ button,
|
||||
}
|
||||
|
||||
&--outlined {
|
||||
border: solid rgba(var(--text-color), 0.5) 0.1rem;
|
||||
border: solid rgba(var(--text-color), 0.3) 0.1rem;
|
||||
background-color: rgba(var(--foreground-color), 1);
|
||||
}
|
||||
&--transparent {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
button:disabled {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
filter: saturate(0);
|
||||
}
|
||||
|
||||
.cta {
|
||||
@ -211,10 +215,6 @@ button,
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
a:any-link:focus-visible {
|
||||
outline: rgba(var(--text-color), 1) 0.1rem solid;
|
||||
}
|
||||
@ -273,27 +273,6 @@ sm-textarea {
|
||||
--max-height: 32ch;
|
||||
}
|
||||
|
||||
sm-button {
|
||||
--padding: 0.8rem;
|
||||
|
||||
&[variant="primary"] {
|
||||
.icon {
|
||||
fill: rgba(var(--background-color), 1);
|
||||
}
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
.icon {
|
||||
fill: rgba(var(--text-color), 0.6);
|
||||
}
|
||||
}
|
||||
|
||||
&.danger {
|
||||
--background: var(--danger-color);
|
||||
color: rgba(var(--background-color), 1);
|
||||
}
|
||||
}
|
||||
|
||||
sm-spinner {
|
||||
--size: 1rem;
|
||||
--stroke-width: 0.1rem;
|
||||
@ -312,14 +291,14 @@ sm-option {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
strip-select {
|
||||
sm-chips {
|
||||
--gap: 0;
|
||||
background-color: rgba(var(--text-color), 0.06);
|
||||
border-radius: 0.3rem;
|
||||
padding: 0.3rem;
|
||||
}
|
||||
|
||||
strip-option {
|
||||
sm-chip {
|
||||
position: relative;
|
||||
font-size: 0.8rem;
|
||||
--border-radius: 0.2rem;
|
||||
@ -660,15 +639,26 @@ ol {
|
||||
display: grid;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
|
||||
justify-items: center;
|
||||
& > * {
|
||||
grid-area: 1/1/2/2;
|
||||
}
|
||||
|
||||
button {
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
.password-field {
|
||||
label {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
input:checked ~ .visible {
|
||||
display: none;
|
||||
}
|
||||
input:not(:checked) ~ .invisible {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.page {
|
||||
height: 100%;
|
||||
@ -709,41 +699,37 @@ ol {
|
||||
#confirmation_popup,
|
||||
#prompt_popup {
|
||||
flex-direction: column;
|
||||
|
||||
h4 {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
sm-button {
|
||||
margin: 0;
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.flex {
|
||||
padding: 0;
|
||||
margin-top: 1rem;
|
||||
|
||||
sm-button:first-of-type {
|
||||
margin-right: 0.6rem;
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#prompt_message {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.popup__header {
|
||||
position: relative;
|
||||
display: grid;
|
||||
gap: 0.5rem;
|
||||
width: 100%;
|
||||
padding: 0 1.5rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.popup__header__close {
|
||||
padding: 0.5rem;
|
||||
margin-left: -0.5rem;
|
||||
& > * {
|
||||
grid-row: 1;
|
||||
}
|
||||
h3,
|
||||
h4 {
|
||||
grid-column: 1/-1;
|
||||
justify-self: center;
|
||||
align-self: center;
|
||||
}
|
||||
&__close {
|
||||
grid-column: 1;
|
||||
margin-left: -1rem;
|
||||
justify-self: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
.flo-icon {
|
||||
@ -768,7 +754,13 @@ ol {
|
||||
}
|
||||
|
||||
.inner-page {
|
||||
background-color: rgba(var(--foreground-color), 0.5);
|
||||
gap: 1rem;
|
||||
display: grid;
|
||||
position: relative;
|
||||
padding: 1rem;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
height: 100%;
|
||||
background-color: rgba(var(--foreground-color), 0.3);
|
||||
}
|
||||
|
||||
#sign_in,
|
||||
@ -778,7 +770,7 @@ ol {
|
||||
|
||||
section {
|
||||
margin-top: -8rem;
|
||||
width: min(24rem, 100%);
|
||||
width: min(26rem, 100%);
|
||||
}
|
||||
|
||||
sm-form {
|
||||
@ -790,28 +782,22 @@ ol {
|
||||
.h2 {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
h5 {
|
||||
color: rgba(var(--text-color), 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
.generated-keys-wrapper {
|
||||
padding: 1rem;
|
||||
background-color: rgba(var(--foreground-color), 1);
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
#flo_id_warning {
|
||||
padding-bottom: 1.5rem;
|
||||
border-bottom: thin solid rgba(var(--text-color), 0.3);
|
||||
|
||||
.icon {
|
||||
height: 4rem;
|
||||
width: 4rem;
|
||||
padding: 1rem;
|
||||
height: 3rem;
|
||||
width: 3rem;
|
||||
padding: 0.8rem;
|
||||
overflow: visible;
|
||||
background-color: #ffc107;
|
||||
border-radius: 3rem;
|
||||
fill: rgba(0, 0, 0, 0.8);
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@ -820,10 +806,6 @@ ol {
|
||||
color: rgba(var(--text-color), 0.8);
|
||||
}
|
||||
|
||||
sm-button[variant="primary"] {
|
||||
--foreground-color: 255, 255, 255;
|
||||
}
|
||||
|
||||
.danger {
|
||||
color: var(--error-color);
|
||||
}
|
||||
@ -1733,7 +1715,7 @@ sm-button[variant="primary"] {
|
||||
#mail_type_selector {
|
||||
width: fit-content;
|
||||
|
||||
strip-option {
|
||||
sm-chip {
|
||||
--padding: 0.5rem 1.2rem;
|
||||
|
||||
.badge {
|
||||
@ -2147,7 +2129,7 @@ sm-button[variant="primary"] {
|
||||
padding: 0 1.5rem;
|
||||
margin-top: 2rem;
|
||||
|
||||
sm-button:first-of-type {
|
||||
button:first-of-type {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
}
|
||||
@ -2311,33 +2293,12 @@ sm-button[variant="primary"] {
|
||||
font-size: 2rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
sm-button {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
#landing_illustration {
|
||||
grid-area: illustration;
|
||||
}
|
||||
|
||||
.frame,
|
||||
#sign_in {
|
||||
form {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.h2 {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
sm-button[variant="primary"] {
|
||||
margin-top: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.fab {
|
||||
bottom: 4rem;
|
||||
}
|
||||
@ -2468,6 +2429,8 @@ sm-button[variant="primary"] {
|
||||
|
||||
#sign_in {
|
||||
width: 24rem;
|
||||
height: auto;
|
||||
border-radius: 0.4rem;
|
||||
}
|
||||
|
||||
#main_page {
|
||||
|
||||
204
index.html
204
index.html
@ -68,9 +68,9 @@
|
||||
<sm-popup id="confirmation_popup">
|
||||
<h4 id="confirm_title"></h4>
|
||||
<p id="confirm_message"></p>
|
||||
<div class="flex align-center">
|
||||
<sm-button variant="no-outline" class="cancel-btn">Cancel</sm-button>
|
||||
<sm-button variant="no-outline" class="submit-btn">OK</sm-button>
|
||||
<div class="flex align-center gap-0-5 margin-left-auto">
|
||||
<button class="button cancel-button">Cancel</button>
|
||||
<button class="button button--primary confirm-button">OK</button>
|
||||
</div>
|
||||
</sm-popup>
|
||||
<sm-popup id="prompt_popup">
|
||||
@ -78,9 +78,9 @@
|
||||
<p id="prompt_message"></p>
|
||||
<sm-form>
|
||||
<sm-input id="prompt_input"></sm-input>
|
||||
<div class="flex align-center">
|
||||
<sm-button variant="no-outline" class="cancel-btn">Cancel</sm-button>
|
||||
<sm-button variant="no-outline" class="submit-btn" type="submit">OK</sm-button>
|
||||
<div class="flex align-center gap-0-5 margin-left-auto">
|
||||
<button class="button cancel-button">Cancel</button>
|
||||
<button class="button confirm-button button--primary" type="submit">OK</button>
|
||||
</div>
|
||||
</sm-form>
|
||||
</sm-popup>
|
||||
@ -109,9 +109,8 @@
|
||||
<h1 class="title-font">
|
||||
Truly Secure, Private and Reliable.
|
||||
</h1>
|
||||
<p class="margin-block-1">A messenger made with Blockchain and Open Source technologies. Take back
|
||||
control of your data that
|
||||
belongs to you and you alone.</p>
|
||||
<p class="margin-block-1">A messenger made with Blockchain and Open Source technologies. Your data
|
||||
remains with you, as it should.</p>
|
||||
<div class="flex">
|
||||
<a href="#/sign_up" class="button">Get started</a>
|
||||
<a href="#/sign_in" class="button button--primary">Sign In</a>
|
||||
@ -121,44 +120,69 @@
|
||||
<img src="assets/message-background.svg" alt="">
|
||||
</div>
|
||||
</div>
|
||||
<article id="sign_in" class="inner-page page-layout hidden">
|
||||
<article id="sign_in" class="inner-page hidden">
|
||||
<section>
|
||||
<h1 style="font-size: 2rem;">Sign In</h1>
|
||||
<h1 style="font-size: 2rem;">Sign in</h1>
|
||||
<p>Welcome back, glad to see you again</p>
|
||||
<sm-form id="sign_in_form">
|
||||
<sm-input id="private_key_field" type="password" placeholder="FLO private key"
|
||||
error-text="Private key is invalid" data-private-key required></sm-input>
|
||||
<sm-button id="sign_in_button" variant="primary" disabled>Sign In</sm-button>
|
||||
<sm-input id="private_key_field" class="password-field" type="password"
|
||||
placeholder="FLO private key" error-text="Private key is invalid" data-private-key required>
|
||||
<label slot="right" class="interact">
|
||||
<input type="checkbox" class="hidden" autocomplete="off" readonly
|
||||
onchange="togglePrivateKeyVisibility(this)">
|
||||
<svg class="icon invisible" xmlns="http://www.w3.org/2000/svg" height="24px"
|
||||
viewBox="0 0 24 24" width="24px" fill="#000000">
|
||||
<title>Hide password</title>
|
||||
<path d="M0 0h24v24H0zm0 0h24v24H0zm0 0h24v24H0zm0 0h24v24H0z" fill="none" />
|
||||
<path
|
||||
d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46C3.08 8.3 1.78 10.02 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78l3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z" />
|
||||
</svg>
|
||||
<svg class="icon visible" xmlns="http://www.w3.org/2000/svg" height="24px"
|
||||
viewBox="0 0 24 24" width="24px" fill="#000000">
|
||||
<title>Show password</title>
|
||||
<path d="M0 0h24v24H0z" fill="none" />
|
||||
<path
|
||||
d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z" />
|
||||
</svg>
|
||||
</label>
|
||||
</sm-input>
|
||||
<button id="sign_in_button" class="button button--primary" type="submit" disabled>Sign in</button>
|
||||
</sm-form>
|
||||
<p>
|
||||
New here? <a href="#/sign_up">get your FLO login credentials</a>
|
||||
</p>
|
||||
</section>
|
||||
</article>
|
||||
<article id="sign_up" class="inner-page page-layout hidden">
|
||||
<article id="sign_up" class="inner-page hidden">
|
||||
<section class="grid gap-1-5">
|
||||
<div id="flo_id_warning" class="grid justify-center gap-0-5">
|
||||
<div id="flo_id_warning" class="flex gap-1">
|
||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px"
|
||||
fill="#000000">
|
||||
<path d="M0 0h24v24H0z" fill="none" />
|
||||
<path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z" />
|
||||
</svg>
|
||||
<h3>Keep your keys safe!</h3>
|
||||
<strong>Don't share with anyone. Once lost private key can't be
|
||||
recovered.</strong>
|
||||
</div>
|
||||
<div class="grid gap-1-5">
|
||||
<div class="grid gap-0-5">
|
||||
<h5>FLO ID</h5>
|
||||
<sm-copy id="generated_flo_id"></sm-copy>
|
||||
<strong>
|
||||
<h3>
|
||||
Keep your keys safe!
|
||||
</h3>
|
||||
</strong>
|
||||
<p>Don't share with anyone. Once lost private key can't be
|
||||
recovered.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid gap-1-5 generated-keys-wrapper">
|
||||
<div class="grid gap-0-5">
|
||||
<h5>FLO address</h5>
|
||||
<sm-copy id="generated_flo_address"></sm-copy>
|
||||
</div>
|
||||
<div class="grid gap-0-5">
|
||||
<h5>Private key</h5>
|
||||
<sm-copy id="generated_private_key"></sm-copy>
|
||||
</div>
|
||||
</div>
|
||||
<sm-button id="sign_up_button" variant="primary">Sign in with these credentials</sm-button>
|
||||
<p>You can use these FLO credentials with FLO Messenger other RanchiMall apps
|
||||
<button id="sign_up_button" class="button button--primary w-100">Sign in with these credentials</button>
|
||||
<p class="margin-top-1">You can use these FLO credentials with other RanchiMall apps
|
||||
too. </p>
|
||||
</section>
|
||||
</article>
|
||||
@ -258,7 +282,7 @@
|
||||
</sm-menu>
|
||||
</div>
|
||||
<sm-input id="search_chats" class="margin-right-0-5" type="search"
|
||||
placeholder="Search FLO ID or name">
|
||||
placeholder="Search FLO address or name">
|
||||
<svg slot="icon" class="icon" xmlns="http://www.w3.org/2000/svg" height="24px"
|
||||
viewBox="0 0 24 24" width="24px" fill="#000000">
|
||||
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||
@ -365,10 +389,10 @@
|
||||
<header class="grid header">
|
||||
<div class="flex align-center space-between">
|
||||
<h4>Mail</h4>
|
||||
<strip-select id="mail_type_selector">
|
||||
<strip-option value="inbox" selected>Inbox </strip-option>
|
||||
<strip-option value="sent">Sent </strip-option>
|
||||
</strip-select>
|
||||
<sm-chips id="mail_type_selector">
|
||||
<sm-chip value="inbox" selected>Inbox </sm-chip>
|
||||
<sm-chip value="sent">Sent </sm-chip>
|
||||
</sm-chips>
|
||||
</div>
|
||||
</header>
|
||||
<button class="button button--primary fab" id="new_mail_button"
|
||||
@ -422,8 +446,8 @@
|
||||
<div id="mail" class="flex hide-on-mobile hidden">
|
||||
<div id="mail_container"></div>
|
||||
<div class="flex">
|
||||
<sm-button id="prev_mail">View Previous Mail</sm-button>
|
||||
<sm-button id="show_reply_popup">reply</sm-button>
|
||||
<button class="button" id="prev_mail">View Previous Mail</button>
|
||||
<button class="button" id="show_reply_popup">reply</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@ -524,7 +548,7 @@
|
||||
<h4>Blocked</h4>
|
||||
<ul id="blocked_list" class="observe-empty-state"></ul>
|
||||
<div class="empty-state">
|
||||
<p>No blocked FLO IDs</p>
|
||||
<p>No blocked FLO addresss</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
@ -713,7 +737,7 @@
|
||||
<sm-form>
|
||||
<sm-input id="add_contact_floID" floId placeholder="FLO address" animate autofocus required></sm-input>
|
||||
<sm-input id="add_contact_name" placeholder="Name" animate required></sm-input>
|
||||
<sm-button id="add_contact_button" variant="primary" disabled>Add</sm-button>
|
||||
<button class="button button--primary" id="add_contact_button" type="submit" disabled>Add</button>
|
||||
</sm-form>
|
||||
</sm-popup>
|
||||
<sm-popup id="compose_mail_popup">
|
||||
@ -727,7 +751,7 @@
|
||||
</svg>
|
||||
</button>
|
||||
<h4>Compose Mail</h4>
|
||||
<p>You can send mail to multiple FLO IDs by entering comma separated IDs </p>
|
||||
<p>You can send mail to multiple FLO addresss by entering comma separated IDs </p>
|
||||
</header>
|
||||
<sm-form>
|
||||
<div id="auto_complete_contact" class="flex flex-direction-column">
|
||||
@ -756,7 +780,7 @@
|
||||
<sm-form>
|
||||
<sm-input id="subject_of_reply_mail" placeholder="Subject" animate></sm-input>
|
||||
<sm-textarea id="reply_mail_content" placeholder="Type a mail" id="" rows="10" required></sm-textarea>
|
||||
<sm-button id="reply_mail_button" variant="primary" disabled>Send</sm-button>
|
||||
<button id="reply_mail_button" class="button button--primary" disabled>Send</button>
|
||||
</sm-form>
|
||||
</sm-popup>
|
||||
<!-- Contact popup -->
|
||||
@ -779,7 +803,7 @@
|
||||
<p id="last_interaction_time"></p>
|
||||
</div>
|
||||
<div class="popup-section">
|
||||
<h5 id="flo_id_type">FLO ID</h5>
|
||||
<h5 id="flo_id_type">FLO address</h5>
|
||||
<sm-copy id="contact_flo_id"></sm-copy>
|
||||
</div>
|
||||
<div id="group_description_card" class="hidden">
|
||||
@ -846,11 +870,11 @@
|
||||
</fieldset>
|
||||
<div class="grid gap-1">
|
||||
<h5>Contacts</h5>
|
||||
<sm-input id="search_contacts" type="search" placeholder="Search name or FLO ID"> </sm-input>
|
||||
<sm-input id="search_contacts" type="search" placeholder="Search name or FLO address"> </sm-input>
|
||||
<div id="contacts_container" class="observe-empty-state"></div>
|
||||
<div class="empty-state">
|
||||
<h4 class="margin-bottom-0-5">No saved contacts</h4>
|
||||
<p>Use 'Add contact' to add new FLO ID as a contact.</p>
|
||||
<p>Use 'Add contact' to add new FLO address as a contact.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -885,7 +909,7 @@
|
||||
<div id="select_contacts_container" class="observe-empty-state"></div>
|
||||
<div class="empty-state">
|
||||
<h4 class="margin-bottom-0-5">No saved contacts.</h4>
|
||||
<p class="margin-bottom-1">Use 'Add contact' to add new FLO ID as a contact.</p>
|
||||
<p class="margin-bottom-1">Use 'Add contact' to add new FLO address as a contact.</p>
|
||||
<button class="button interactive" onclick="openPopup('add_contact_popup')">
|
||||
<svg class="icon margin-right-0-5" xmlns="http://www.w3.org/2000/svg"
|
||||
enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px"
|
||||
@ -1100,10 +1124,10 @@
|
||||
<div class="grid gap-0-5">
|
||||
<div class="flex align-center space-between">
|
||||
<h4>Fees</h4>
|
||||
<strip-select id="fees_selector" class="hidden">
|
||||
<strip-option value="suggested" selected>Suggested</strip-option>
|
||||
<strip-option value="custom">Custom</strip-option>
|
||||
</strip-select>
|
||||
<sm-chips id="fees_selector" class="hidden">
|
||||
<sm-chip value="suggested" selected>Suggested</sm-chip>
|
||||
<sm-chip value="custom">Custom</sm-chip>
|
||||
</sm-chips>
|
||||
</div>
|
||||
<p id="selected_fee_tip" class="hidden">Estimated time of confirmation is 1hr</p>
|
||||
<sm-input type="number" id="send_fee" placeholder="Fee" min="0.00000001" step="0.00000001"
|
||||
@ -1319,15 +1343,14 @@
|
||||
function openPopup(popupId, pinned) {
|
||||
zIndex++
|
||||
getRef(popupId).setAttribute('style', `z-index: ${zIndex}`)
|
||||
getRef(popupId).show({ pinned })
|
||||
return getRef(popupId);
|
||||
return getRef(popupId).show({ pinned })
|
||||
}
|
||||
|
||||
// hides the popup or modal
|
||||
function closePopup() {
|
||||
function closePopup(options = {}) {
|
||||
if (popupStack.peek() === undefined)
|
||||
return;
|
||||
popupStack.peek().popup.hide()
|
||||
popupStack.peek().popup.hide(options)
|
||||
}
|
||||
|
||||
function getFloIdType(floID) {
|
||||
@ -1417,7 +1440,7 @@
|
||||
getRef('contact_details_popup').classList.add('is-group');
|
||||
removeClass(['#group_members_card', '#group_description_card'], 'hidden')
|
||||
getRef('edit_group_button').dataset.groupId = floID;
|
||||
getRef('flo_id_type').textContent = 'Group FLO ID'
|
||||
getRef('flo_id_type').textContent = 'Group FLO address'
|
||||
toggleAttr(getRef('contact_name'), !isAdmin, 'disabled')
|
||||
toggleAttr(getRef('group_description'), !isAdmin, 'disabled')
|
||||
conditionalClass(getRef('edit_group_button'), !isAdmin, 'hidden')
|
||||
@ -1428,10 +1451,10 @@
|
||||
getRef('contact_details_popup').classList.add('is-group');
|
||||
removeClass(['#group_members_card'], 'hidden')
|
||||
addClass(['#group_members_tip', '#group_description_card'], 'hidden')
|
||||
getRef('flo_id_type').textContent = 'Pipeline FLO ID'
|
||||
getRef('flo_id_type').textContent = 'Pipeline FLO address'
|
||||
getRef("last_interaction_time").textContent = ``;
|
||||
} else {
|
||||
getRef('flo_id_type').textContent = 'FLO ID'
|
||||
getRef('flo_id_type').textContent = 'FLO address'
|
||||
getRef('contact_name').disabled = false
|
||||
getRef('contact_initial').textContent = getContactName(floID).charAt(0)
|
||||
getRef("last_interaction_time").textContent = ``;
|
||||
@ -1517,6 +1540,7 @@
|
||||
if (popupStack.items.length === 0) {
|
||||
getRef('main_page').removeAttribute('inert')
|
||||
}
|
||||
zIndex--;
|
||||
})
|
||||
window.addEventListener('popstate', e => {
|
||||
if (!e.state) return
|
||||
@ -1559,49 +1583,59 @@
|
||||
// displays a popup for asking permission. Use this instead of JS confirm
|
||||
const getConfirmation = (title, options = {}) => {
|
||||
return new Promise(resolve => {
|
||||
const { message = '', cancelText = 'Cancel', confirmText = 'OK' } = options
|
||||
openPopup('confirmation_popup', true)
|
||||
const { message = '', cancelText = 'Cancel', confirmText = 'OK', danger = false } = options
|
||||
getRef('confirm_title').innerText = title;
|
||||
getRef('confirm_message').innerText = message;
|
||||
let cancelButton = getRef('confirmation_popup').children[2].children[0],
|
||||
submitButton = getRef('confirmation_popup').children[2].children[1]
|
||||
submitButton.textContent = confirmText
|
||||
const cancelButton = getRef('confirmation_popup').querySelector('.cancel-button');
|
||||
const confirmButton = getRef('confirmation_popup').querySelector('.confirm-button')
|
||||
confirmButton.textContent = confirmText
|
||||
cancelButton.textContent = cancelText
|
||||
submitButton.onclick = () => {
|
||||
closePopup()
|
||||
resolve(true);
|
||||
if (danger)
|
||||
confirmButton.classList.add('button--danger')
|
||||
else
|
||||
confirmButton.classList.remove('button--danger')
|
||||
const { opened, closed } = openPopup('confirmation_popup')
|
||||
confirmButton.onclick = () => {
|
||||
closePopup({ payload: true })
|
||||
}
|
||||
cancelButton.onclick = () => {
|
||||
closePopup()
|
||||
resolve(false);
|
||||
}
|
||||
closed.then((payload) => {
|
||||
confirmButton.onclick = null
|
||||
cancelButton.onclick = null
|
||||
if (payload)
|
||||
resolve(true)
|
||||
else
|
||||
resolve(false)
|
||||
})
|
||||
})
|
||||
}
|
||||
// displays a popup for asking user input. Use this instead of JS prompt
|
||||
function getPromptInput(title, message = '', options = {}) {
|
||||
let { placeholder = '', isPassword = false, cancelText = 'Cancel', confirmText = 'OK' } = options
|
||||
openPopup('prompt_popup', true)
|
||||
getRef('prompt_title').innerText = title;
|
||||
getRef('prompt_message').innerText = message;
|
||||
let buttons = getRef('prompt_popup').querySelectorAll("sm-button");
|
||||
const cancelButton = getRef('prompt_popup').querySelector('.cancel-button');
|
||||
const confirmButton = getRef('prompt_popup').querySelector('.confirm-button')
|
||||
if (isPassword) {
|
||||
placeholder = 'Password'
|
||||
getRef('prompt_input').setAttribute("type", "password")
|
||||
}
|
||||
getRef('prompt_input').setAttribute("placeholder", placeholder)
|
||||
getRef('prompt_input').focusIn()
|
||||
buttons[0].textContent = cancelText;
|
||||
buttons[1].textContent = confirmText;
|
||||
cancelButton.textContent = cancelText;
|
||||
confirmButton.textContent = confirmText;
|
||||
openPopup('prompt_popup', true)
|
||||
return new Promise((resolve, reject) => {
|
||||
buttons[0].onclick = () => {
|
||||
cancelButton.addEventListener('click', () => {
|
||||
closePopup()
|
||||
return (null);
|
||||
}
|
||||
buttons[1].onclick = () => {
|
||||
const value = getRef('prompt_input').value;
|
||||
return null
|
||||
}, { once: true })
|
||||
confirmButton.addEventListener('click', () => {
|
||||
closePopup()
|
||||
resolve(value)
|
||||
}
|
||||
resolve(getRef('prompt_input').value)
|
||||
}, { once: true })
|
||||
})
|
||||
}
|
||||
|
||||
@ -1697,8 +1731,8 @@
|
||||
notify('copied', 'success')
|
||||
})
|
||||
document.addEventListener("pointerdown", (e) => {
|
||||
if (e.target.closest("button:not([disabled]), sm-button:not([disabled]), .interactive")) {
|
||||
createRipple(e, e.target.closest("button, sm-button, .interactive"));
|
||||
if (e.target.closest("button:not([disabled]), .interactive")) {
|
||||
createRipple(e, e.target.closest("button, .interactive"));
|
||||
}
|
||||
});
|
||||
document.querySelectorAll('.popup__header__close, .close-popup-on-click').forEach(elem => {
|
||||
@ -1758,6 +1792,7 @@
|
||||
const { firstLoad } = options
|
||||
let pageId
|
||||
let subPageId1
|
||||
let subPageId2
|
||||
let searchParams
|
||||
let params
|
||||
if (targetPage === '') {
|
||||
@ -1772,9 +1807,9 @@
|
||||
if (targetPage.includes('?')) {
|
||||
const splitAddress = targetPage.split('?')
|
||||
searchParams = splitAddress.pop();
|
||||
[, pageId, subPageId1] = splitAddress.pop().split('/')
|
||||
[, pageId, subPageId1, subPageId2] = splitAddress.pop().split('/')
|
||||
} else {
|
||||
[, pageId, subPageId1] = targetPage.split('/')
|
||||
[, pageId, subPageId1, subPageId2] = targetPage.split('/')
|
||||
}
|
||||
} else {
|
||||
pageId = targetPage
|
||||
@ -1806,7 +1841,7 @@
|
||||
break;
|
||||
case 'sign_up':
|
||||
const { floID, privKey } = floCrypto.generateNewID()
|
||||
getRef('generated_flo_id').value = floID
|
||||
getRef('generated_flo_address').value = floID
|
||||
getRef('generated_private_key').value = privKey
|
||||
break;
|
||||
case 'chat_page':
|
||||
@ -2287,6 +2322,11 @@
|
||||
}
|
||||
})
|
||||
}
|
||||
function togglePrivateKeyVisibility(input) {
|
||||
const target = input.closest('sm-input')
|
||||
target.type = target.type === 'password' ? 'text' : 'password';
|
||||
target.focusIn()
|
||||
}
|
||||
function getSignedIn(passwordType) {
|
||||
return new Promise((resolve, reject) => {
|
||||
routeTo(window.location.hash)
|
||||
@ -2342,7 +2382,7 @@
|
||||
}
|
||||
}
|
||||
function signOut() {
|
||||
getConfirmation('Sign out?', 'You are about to sign out of the app, continue?', 'Stay', 'Leave')
|
||||
getConfirmation('Sign out?', { message: 'You are about to sign out of the app, continue?', confirmText: 'Leave', cancelText: 'Stay' })
|
||||
.then(async (res) => {
|
||||
if (res) {
|
||||
await floDapps.clearCredentials();
|
||||
@ -3557,7 +3597,7 @@
|
||||
let floID = getRef('add_contact_floID').value.trim();
|
||||
let name = getRef('add_contact_name').value.trim();
|
||||
if (floID === floDapps.user.id) {
|
||||
notify(`you can't add your own FLO ID as contact`, 'error')
|
||||
notify(`you can't add your own FLO address as contact`, 'error')
|
||||
return
|
||||
}
|
||||
if (floGlobals.contacts.hasOwnProperty(floID)) {
|
||||
@ -4000,18 +4040,18 @@
|
||||
}
|
||||
|
||||
function blockUser() {
|
||||
getConfirmation('Block this FLO ID?', { message: `Are you sure to block this FLO ID?`, confirmText: 'Block', cancelText: 'Cancel' }).then(confirmed => {
|
||||
getConfirmation('Block this FLO address?', { message: `Are you sure to block this FLO address?`, confirmText: 'Block', cancelText: 'Cancel' }).then(confirmed => {
|
||||
if (confirmed) {
|
||||
messenger.blockUser(floGlobals.activeFloID).then(result => {
|
||||
getChatCard(floGlobals.activeFloID).querySelector('.last-message').textContent = 'This user is blocked'
|
||||
closePopup()
|
||||
notify('FLO ID blocked', 'success')
|
||||
notify('FLO address blocked', 'success')
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
function unblockUser(floID) {
|
||||
getConfirmation('Unblock this FLO ID?', { message: `Are you sure to unblock this FLO ID?`, confirmText: 'Unblock', cancelText: 'Cancel' }).then(confirmed => {
|
||||
getConfirmation('Unblock this FLO address?', { message: `Are you sure to unblock this FLO address?`, confirmText: 'Unblock', cancelText: 'Cancel' }).then(confirmed => {
|
||||
if (confirmed) {
|
||||
messenger.unblockUser(floID || floGlobals.activeFloID).then(result => {
|
||||
const chatCard = getChatCard(floID || floGlobals.activeFloID);
|
||||
@ -4021,7 +4061,7 @@
|
||||
console.log(error)
|
||||
})
|
||||
chatCard.querySelector('.last-message').textContent = 'This user is unblocked'
|
||||
notify('FLO ID unblocked', 'success')
|
||||
notify('FLO address unblocked', 'success')
|
||||
closePopup()
|
||||
render.blockedList()
|
||||
renderChatList(messenger.getChatOrder())
|
||||
|
||||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user