diff options
| author | 2022-09-29 12:02:41 +0200 | |
|---|---|---|
| committer | 2022-09-29 12:02:41 +0200 | |
| commit | 938328cd077d40b75e0834d56ff8d43ad035fd2b (patch) | |
| tree | 76ed59a9adf8a40e83c99a3ea34ce7cb5a5f8877 /web/source | |
| parent | [chore] simplify generating log entry caller information (#863) (diff) | |
| download | gotosocial-938328cd077d40b75e0834d56ff8d43ad035fd2b.tar.xz | |
[frontend] Unified panels (#812)
* settings panel restructuring
* clean up old Gin handlers
* colorscheme redesign, some other small css tweaks
* basic router layout, error boundary
* colorscheme redesign, some other small css tweaks
* kebab-case consistency
* superfluous padding on applist
* remove unused consts
* redux, whitespace changes..
* use .jsx extensions for components
* login flow up till app registration
* full redux oauth implementation, with basic error handling
* split oauth api functions
* oauth api revocation handling
* basic profile change submission
* move old dir
* profile overview
* fix keeping track of the wrong instance url (for different instance/api domains)
* use redux state for profile form
* delete old/index.js, old/basic.js, fully implemented
* implement old/user/profile.js
* implement password change
* remove debug logging
* support future api for removing files
* customize profile css
* remove unneeded wrapper components
* restructure form fields
* start on admin pages
* admin panel settings
* admin settings panel
* remove old/admin files
* add top-level redirect
* refactor/cleanup forms
* only do API checks on logged-in state
* admin-status based routing
* federation block routing
* federation blocks
* upgrade dependencies
* react 18 changes
* media cleanup
* fix useEffect hooks
* remove unused require
* custom emoji base
* emoji uploader
* delete last old panel files
* sidebar styling, remove unused page
* refactor submit functions
* fix sidebar boxshadow-border
* fix old css variables
* fix fake-toot avatar
* fix non-square emoji
* fix user settings redux keys
* properly get admin account contact from instance response
* Account.source default values
* source.status_format key
* mobile responsiveness
* mobile element tweaks
* proper redirect after removing block
* add redirects for old setting panel urls
* deletes
* fix mobile overflow
* clean up debug logging calls
Diffstat (limited to 'web/source')
52 files changed, 3957 insertions, 2791 deletions
| diff --git a/web/source/css/_colors.css b/web/source/css/_colors.css index 1544e6ad0..fb07758aa 100644 --- a/web/source/css/_colors.css +++ b/web/source/css/_colors.css @@ -23,57 +23,85 @@  /* Color definitions */ -$near_white: #fafaff; +/* Foreground */ +$white1: #fafaff; /* default text color, contrast >= 5.0 with all $grays */ +$white2: #b3b5c6; /* less important text, can be used with $gray1 (6.8), $gray2 (5.5), $gray3 (4.9), $gray4 (4.5) */ -$sloth_gray1: #b0b0b5; -$sloth_gray2: #4d4e56; +/* Background shades, contrast >= 5.0 with $white1 (#fafaff) */ +$gray1: #2a2b2f; +$gray2: #35363b; +$gray3: #3a3b41; +$gray4: #45464e; +$gray5: #4d4e56; +$gray6: #575861; +$gray7: #5d5e67; +$gray8: #696a75; -$sloth_orange1: #e78e5a; -$sloth_orange2: #D87841; -$blue: #63b1de; // complementary color to $sloth_orange1 +$orange1: #fd6a00; /* Used for non-text accent colors, can be used as background: $gray1 for text color (contrast 4.6)*/ +$orange2: #ff853e; /* hover/selected accent to $orange1, can be used with $gray1 (5.7), $gray2 (4.6) */ -/* derivative colors */ +$blue1: #3a9fde; /* darker blue for smaller elements (borders), can only be used with $gray1 (4.7) */ +$blue2: #66befe; /* all-round accent color, can be used with $gray1 (6.8), $gray2 (5.5), $gray3 (4.9), $gray4 (4.5) */ +$blue3: #89caff; /* hover/selected accent to $blue2, can be used with $gray1 (7.9), $gray2 (6.3), $gray3 (5.6), $gray4 (5.2), $gray5 (4.7) */ -$sloth_gray2_darker3: color-mod($sloth_gray2 lightness(-3%)); -$sloth_gray2_darker5: color-mod($sloth_gray2 lightness(-5%)); -$sloth_gray2_darker7: color-mod($sloth_gray2 lightness(-7%)); -$sloth_gray2_darker15: color-mod($sloth_gray2 lightness(-15%)); -$sloth_gray2_lighter3: color-mod($sloth_gray2 lightness(+3%)); -$sloth_gray2_lighter5: color-mod($sloth_gray2 lightness(+5%)); +$error1: #860000; /* Error border/foreground text, can be used with $error2 (5.0), $white1 (10), $white2 (5.1) */ +$error2: #ff9796; /* Error background text, can be used with $error1 (5.0), $gray1 (6.6), $gray2 (5.3), $gray3 (4.8) */ +$error-link: #185F8C; /* Error link text, can be used with $error2 (5.54) */ -$blue_lighter8: color-mod($blue lightness(+4%)); -$lightblue: color-mod($blue lightness(+16%)); +$fg: $white1; +$bg: $gray1; -$fg: $near_white; -$bg: $sloth_gray2_darker7; +$bg-trans: color-mod($gray5 alpha(62%)); -$bg_trans: color-mod($sloth_gray2 alpha(62%)); - -$bg_accent: $sloth_gray2_lighter3; -$fg_accent: $lightblue; -$border_accent: $sloth_orange2; +$bg-accent: $gray5; +$fg-accent: $blue3; +$fg-reduced: $white2; +$border-accent: $orange2;  /* Color variables as used in a specific location */ -$footer_bg: $bg_accent; +$link-fg: $fg-accent; + +$button-bg: $blue2; +$button-fg: $gray1; +$button-hover-bg: $blue3; -$link_fg: $fg_accent; +$button-danger-bg: $orange1; +$button-danger-fg: $gray1; +$button-danger-hover-bg: $orange2; -$button_border: 0.08rem solid color-mod($sloth_orange2 lightness(-15%)); -$button_bg: $blue_lighter8; -$button_fg: $sloth_gray2_darker15; -$button_hover_bg: $lightblue; +$toot-focus-bg: $gray5; +$toot-unfocus-bg: $gray3; -$status_focus_bg: $bg_accent; -$status_unfocus_bg: $sloth_gray2_darker3; -$status_info_fg: #CBCBD7; +$toot-info-bg: $gray4; -$bg_no_img_desc: $sloth_orange2; -$bg_sensitive: $sloth_gray2_darker15; +$no-img-desc-bg: $orange1; +$no-img-desc-fg: $gray1; + +$bg-sensitive: $gray1;  $boxshadow: 0 0.4rem 1rem -0.1rem rgba(0,0,0,0.15); -$boxshadow_border: 0.08rem solid $sloth_gray2_darker5; +$boxshadow-border: 0.08rem solid $gray1; + +$avatar-border: $orange2; + +$input-bg: $gray4; +$input-disabled-bg: $gray2; +$input-border: $blue1; +$input-focus-border: $blue3; + +$settings-nav-bg: $bg-accent; +$settings-nav-header-fg: $gray1; +$settings-nav-header-bg: $orange1; + +$settings-nav-bg-hover: $gray3; +/* $settings-nav-fg-hover: $gray1; */ + +$settings-nav-bg-active: $gray2; +/* $settings-nav-fg-active: $orange2; */ -$profile_avatar_border: 0.2rem solid $border_accent; +$error-fg: $error1; +$error-bg: $error2; -$input_bg: $sloth_gray2_darker3;
\ No newline at end of file +$settings-entry-bg: $gray3; +$settings-entry-hover-bg: $gray4;
\ No newline at end of file diff --git a/web/source/css/base.css b/web/source/css/base.css index 3cdf19fe8..d8a79685e 100644 --- a/web/source/css/base.css +++ b/web/source/css/base.css @@ -34,7 +34,7 @@  $br: 0.4rem;  // border radius for items that are framed/bordered  // inside something with $br, eg avatar, header img -$br_inner: 0.2rem;  +$br-inner: 0.2rem;   html, body {  	padding: 0; @@ -42,7 +42,7 @@ html, body {  	background: $bg;  	color: $fg;  	font-family: "Noto Sans", sans-serif; -	scrollbar-color: $sloth_orange1 $sloth_gray2_darker3; +	scrollbar-color: $orange1 $gray3;  }  body { @@ -71,7 +71,7 @@ h1 {  }  a { -	color: $link_fg; +	color: $link-fg;  }  header, footer { @@ -83,9 +83,13 @@ header, footer {  	align-self: start;  } +header { +	display: flex; +	justify-content: center; +} +  header a {  	margin: 2rem; -	/* background: $header_bg; */  	display: flex;  	flex-direction: column;  	flex-wrap: wrap; @@ -109,7 +113,7 @@ header a {  	}  } -.excerpt_top { +.excerpt-top {  	margin-top: -1rem;  	margin-bottom: 2rem;  	font-style: italic; @@ -119,15 +123,15 @@ header a {  	.count {  		font-weight: bold; -		color: $fg_accent; +		color: $fg-accent;  	}  }  main {  	section { -		background: $bg_accent; +		background: $bg-accent;  		box-shadow: $boxshadow; -		border: $boxshadow_border; +		border: $boxshadow-border;  		border-radius: $br;  		padding: 2rem;  		margin-bottom: 2rem; @@ -144,10 +148,10 @@ main {  .button, button {  	border-radius: 0.2rem; -	color: $button_fg; -	background: $button_bg; +	color: $button-fg; +	background: $button-bg;  	box-shadow: $boxshadow; -	border: $button_border; +	border: $button-border;  	text-decoration: none;  	font-size: 1.2rem;  	font-weight: bold; @@ -157,8 +161,17 @@ main {  	text-align: center;  	font-family: 'Noto Sans', sans-serif; +	&.danger { +		color: $button-danger-fg; +		background: $button-danger-bg; + +		&:hover { +			background: $button-danger-hover-bg; +		} +	} +  	&:hover { -		background: $button_hover_bg; +		background: $button-hover-bg;  	}  } @@ -191,7 +204,7 @@ section.apps {  			grid-template-columns: 25% 1fr;  			gap: 1.5rem;  			padding: 0.5rem; -			background: $bg_accent; +			background: $bg-accent;  			border-radius: 0.5rem;  			.logo { @@ -211,7 +224,7 @@ section.apps {  			}  			div { -				padding: 1rem 0; +				padding: 0;  				h3 {  					margin-top: 0;  				} @@ -264,26 +277,42 @@ section.error {  	}  } +.error-text { +	color: $error1; +	background: $error2; +	border-radius: 0.1rem; +	font-weight: bold; +} +  input, select, textarea {  	box-sizing: border-box; -	border: 0.15rem solid $border_accent; +	border: 0.15rem solid $input-border;  	border-radius: 0.1rem;  	color: $fg; -	/* background: $input_bg; */ -	background: $bg_accent; +	background: $input-bg;  	width: 100%;  	font-family: 'Noto Sans', sans-serif;  	font-size: 1rem;  	padding: 0.3rem;  	&:focus { -		border-color: $fg_accent; +		border-color: $input-focus-border; +	} + +	&:disabled { +		background: $input-disabled-bg;  	}  } -input, textarea { -	padding-top: 0.1rem; -	padding-bottom: 0.1rem; +::placeholder { +	opacity: 1; +	color: $fg-reduced +} + +hr { +	color: transparent; +	width: 100%; +	border-bottom: 0.02rem solid $border-accent;  }  footer { @@ -330,4 +359,8 @@ footer {  	margin: -0.5ex 0 0;  	object-fit: contain;  	vertical-align: middle; +} + +.monospace { +	font-family: monospace;  }
\ No newline at end of file diff --git a/web/source/css/profile.css b/web/source/css/profile.css index 4fa0b6247..01ec077ac 100644 --- a/web/source/css/profile.css +++ b/web/source/css/profile.css @@ -28,7 +28,7 @@ main {  }  .profile { -	background: $bg_accent; +	background: $bg-accent;  	display: grid;  	grid-template-rows: auto auto auto;  	grid-template-columns: auto; @@ -38,7 +38,7 @@ main {  	border-radius: $br;  	box-shadow: $boxshadow; -	border: $boxshadow_border; +	border: $boxshadow-border;  	.headerimage {  		width: 100%; @@ -50,7 +50,7 @@ main {  			width: 100%;  			height: 100%;  			object-fit: cover; -			border-radius: $br_inner $br_inner 0 0; +			border-radius: $br-inner $br-inner 0 0;  		}  	} @@ -69,7 +69,7 @@ main {  		#profile-basic-filler2 {  			grid-area: filler2; -			background: $bg_trans; +			background: $bg-trans;  		}  		.avatar { @@ -79,7 +79,7 @@ main {  			width: 8.5rem;  			grid-area: avatar;  			background: $bg; -			border: $profile_avatar_border; +			border: 0.2rem solid $avatar-border;  			padding: 0;  			border-radius: $br;  			position: relative; @@ -87,7 +87,7 @@ main {  			box-shadow: $boxshadow;  			img {  				object-fit: cover; -				border-radius: $br_inner; +				border-radius: $br-inner;  				width: 100%;  				height: 100%;  			} @@ -105,7 +105,7 @@ main {  			font-weight: bold;  			font-size: 2rem;  			line-height: 2.2rem; -			background: $bg_trans; +			background: $bg-trans;  			word-break: break-all;  			text-overflow: ellipsis;  			overflow: hidden; @@ -120,7 +120,7 @@ main {  			padding-top: 0;  			margin-top: 0.25rem;  			padding-bottom: 0.25rem; -			color: $fg_accent; +			color: $fg-accent;  			font-weight: bold;  			word-break: break-all;  			text-overflow: ellipsis; diff --git a/web/source/css/status.css b/web/source/css/status.css index 9a83a7fd5..e34b3b091 100644 --- a/web/source/css/status.css +++ b/web/source/css/status.css @@ -31,13 +31,13 @@ main {  }  .toot { -	background: $status_unfocus_bg; +	background: $toot-unfocus-bg;  	box-shadow: $boxshadow; -	border: $boxshadow_border; +	border: $boxshadow-border;  	position: relative;  	margin-bottom: $br; -	border-radius: $br; -	padding: 1.5rem 0; +	padding-top: 1.5rem; +	padding-bottom: 0.7rem;  	a {  		position: relative; @@ -49,27 +49,34 @@ main {  	.contentgrid {  		padding: 0 1.5rem;  		display: grid; -		grid-template-columns: 4rem auto 1fr; -		grid-template-rows: 1.5rem auto auto; +		grid-template-columns: 4rem 1fr auto; +		grid-template-rows: 1.5rem auto auto auto;  		column-gap: 0.5rem;  	} +	.not-expanded { +		color: $fg-reduced; +		grid-column: 3; +		grid-row: 1; +	} +  	.avatar { -		grid-row: span 2; +		grid-row: span 3;  		aspect-ratio: 1/1; +		display: flex; +		border: 0.2rem solid $avatar-border; +		border-radius: 0.4rem; +		overflow: hidden; /* hides corners from img overflowing */  		img {  			height: 100%;  			width: 100%;  			object-fit: cover;  			background: $bg; -			border: 0.1rem solid $acc2; -			border-radius: calc($br / 1.5);  		}  	}  	.displayname { -		grid-column: span 2;  		font-weight: bold;  		font-size: 1.2rem;  		line-height: 2rem; @@ -82,7 +89,7 @@ main {  	}  	.username { -		color: $link_fg; +		color: $link-fg;  		line-height: 2rem;  		margin-top: -0.5rem;  		align-self: start; @@ -119,8 +126,7 @@ main {  	.text {  		margin: 0; -		margin-top: 0.5rem; -		grid-column: span 3; +		grid-column: 2 / span 2;  		grid-row: span 1;  		overflow: hidden; @@ -128,34 +134,33 @@ main {  		z-index: 2;  		a { -			color: $link_fg; +			color: $link-fg;  			text-decoration: underline;  		}  		.content { -			padding-top: 0.5rem;  			padding-bottom: 0.5rem;  			word-break: break-word;  			blockquote {  				padding: 0.5rem 0 0.5rem 1.5rem; -				border-left: 0.2rem solid $sloth_orange1; +				border-left: 0.2rem solid $border-accent;  				margin-left: 1rem;  				font-style: italic;  			}  			hr { -				border: 1px dashed $sloth_orange1; +				border: 1px dashed $border-accent;  			}   			pre, code { -				background-color: $sloth_gray2_darker7; +				background-color: $gray2;  			}  			code {  				padding: 0.25rem; -				border-radius: $br_inner; +				border-radius: $br-inner;  			}  			pre { @@ -249,7 +254,7 @@ main {  			.closed {  				transition: 0.3s; -				background: $bg_sensitive; +				background: $bg-sensitive;  				@supports (backdrop-filter: blur(2rem)) {  					background: transparent;  					backdrop-filter: blur(2rem); @@ -263,17 +268,17 @@ main {  		}  		.no-image-desc { -			color: $button_fg; +			color: $no-img-desc-fg; +			background: $no-img-desc-bg;  			display: flex;  			position: absolute;  			bottom: 0.1rem;  			right: 0.4rem;  			margin-bottom: 0.4rem;  			margin-right: 0.4rem; -			background: $bg_no_img_desc;  			padding: 0.1rem 0.45rem;  			border-radius: 100%; -			border: 0.2rem solid $button_fg; +			border: 0.2rem solid $button-fg;  			z-index: 3;  			i.fa { @@ -302,12 +307,13 @@ main {  	}  	.info { +		background: $toot-info-bg; +		color: $fg-reduced;  		display: none; -		border-top: 0.15rem solid $status_unfocus_bg; +		border-top: 0.15rem solid $toot-info-border;  		padding: 0.5rem 1.5rem;  		div { -			position: relative;  			padding-right: 1.3rem;  		} @@ -317,30 +323,6 @@ main {  		grid-column: span 3;  		flex-wrap: wrap; - -		div.stats::after { -			display: none; -		} - -		div::after { -			$size: 0.25rem; -			display: block; -			background: $fg_dark; -			height: $size; -			width: $size; -			content: ""; -			position: absolute; -			top: calc((1.5rem - $size) / 2); -			right: 0.55rem; -			border-radius: 1rem; -		} - -		div:last-child { -			&::after { -				display: none; -			} -			margin-right: 0; -		}  	}  	.toot-link { @@ -362,7 +344,7 @@ main {  		border-top-right-radius: $br;  	} -	&:last-child { +	&:last-child, &:last-child .info {  		/* bottom left, bottom right */  		border-bottom-left-radius: $br;  		border-bottom-right-radius: $br; @@ -370,11 +352,21 @@ main {  	}  	&.expanded { -		background: $status_focus_bg; +		background: $toot-focus-bg;  		padding-bottom: 0;  		.contentgrid { -			padding-bottom: 1rem; +			.displayname { +				grid-column: span 2; +			} + +			.text { +				grid-column: 1 / span 3; +			} + +			.not-expanded { +				display: none; +			}  		}  		.info { diff --git a/web/source/dev-server.js b/web/source/dev-server.js index 3802eb88c..46baad24a 100644 --- a/web/source/dev-server.js +++ b/web/source/dev-server.js @@ -1,19 +1,19 @@  /* -   GoToSocial -   Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org +	GoToSocial +	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org -   This program is free software: you can redistribute it and/or modify -   it under the terms of the GNU Affero General Public License as published by -   the Free Software Foundation, either version 3 of the License, or -   (at your option) any later version. +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU Affero General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. -   This program is distributed in the hope that it will be useful, -   but WITHOUT ANY WARRANTY; without even the implied warranty of -   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -   GNU Affero General Public License for more details. +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU Affero General Public License for more details. -   You should have received a copy of the GNU Affero General Public License -   along with this program.  If not, see <http://www.gnu.org/licenses/>. +	You should have received a copy of the GNU Affero General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>.  */  "use strict"; diff --git a/web/source/frontend/index.js b/web/source/frontend/index.js index 5c53a31bf..2a54d52b6 100644 --- a/web/source/frontend/index.js +++ b/web/source/frontend/index.js @@ -18,11 +18,6 @@  "use strict"; - -// WARNING: currently dependencies get deduplicated with factor-bundle, but  -// our frontend templates don't load the common bundle.js since it contains React etc -// so we can't use any dependencies that would deduplicate with the other files -  const Photoswipe = require("photoswipe/dist/umd/photoswipe.umd.min.js");  const PhotoswipeLightbox = require("photoswipe/dist/umd/photoswipe-lightbox.umd.min.js");  const PhotoswipeCaptionPlugin = require("photoswipe-dynamic-caption-plugin").default; diff --git a/web/source/index.js b/web/source/index.js index 20e8ee623..218e420ed 100644 --- a/web/source/index.js +++ b/web/source/index.js @@ -1,19 +1,19 @@  /* -   GoToSocial -   Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org +	GoToSocial +	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org -   This program is free software: you can redistribute it and/or modify -   it under the terms of the GNU Affero General Public License as published by -   the Free Software Foundation, either version 3 of the License, or -   (at your option) any later version. +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU Affero General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. -   This program is distributed in the hope that it will be useful, -   but WITHOUT ANY WARRANTY; without even the implied warranty of -   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -   GNU Affero General Public License for more details. +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU Affero General Public License for more details. -   You should have received a copy of the GNU Affero General Public License -   along with this program.  If not, see <http://www.gnu.org/licenses/>. +	You should have received a copy of the GNU Affero General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>.  */  "use strict"; @@ -23,7 +23,8 @@  */  const path = require('path'); -const budoExpress = require('budo-express'); +// Forked budo-express supports EventEmitter, to write bundle.js to disk in development +const budoExpress = require('@f0x52/budo-express');  const babelify = require('babelify');  const fs = require("fs");  const EventEmitter = require('events'); @@ -38,8 +39,9 @@ const splitCSS = require("./lib/split-css.js");  const bundles = {  	"./frontend/index.js": "frontend.js", -	"./panels/admin/index.js": "admin-panel.js", -	"./panels/user/index.js": "user-panel.js", +	"./settings-panel/index.js": "settings.js", +	// "./panels/admin/index.js": "admin-panel.js", +	// "./panels/user/index.js": "user-panel.js",  };  const postcssPlugins = [ @@ -50,6 +52,18 @@ const postcssPlugins = [  	"postcss-color-mod-function"  ].map((plugin) => require(plugin)()); +let uglifyifyInProduction; + +if (process.env.NODE_ENV != "development") { +	console.log("uglifyify'ing production bundles"); +	uglifyifyInProduction = [ +		require("uglifyify"), { +			global: true, +			exts: ".js" +		} +	]; +} +  const browserifyConfig = {  	transform: [  		[ @@ -69,10 +83,7 @@ const browserifyConfig = {  				exclude: /node_modules\/(?!photoswipe-dynamic-caption-plugin)/,  			}  		], -		[require("uglifyify"), { -			global: true, -			exts: ".js" -		}] +		uglifyifyInProduction  	],  	plugin: [  		[require("icssify"), { @@ -86,7 +97,8 @@ const browserifyConfig = {  				return out(file);  			})  		}] -	] +	], +	extensions: [".js", ".jsx", ".css"]  };  const entryFiles = Object.keys(bundles); diff --git a/web/source/lib/split-css.js b/web/source/lib/split-css.js index fb8694095..da5602e1c 100644 --- a/web/source/lib/split-css.js +++ b/web/source/lib/split-css.js @@ -1,19 +1,19 @@  /* -   GoToSocial -   Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org +	GoToSocial +	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org -   This program is free software: you can redistribute it and/or modify -   it under the terms of the GNU Affero General Public License as published by -   the Free Software Foundation, either version 3 of the License, or -   (at your option) any later version. +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU Affero General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. -   This program is distributed in the hope that it will be useful, -   but WITHOUT ANY WARRANTY; without even the implied warranty of -   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -   GNU Affero General Public License for more details. +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU Affero General Public License for more details. -   You should have received a copy of the GNU Affero General Public License -   along with this program.  If not, see <http://www.gnu.org/licenses/>. +	You should have received a copy of the GNU Affero General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>.  */  "use strict"; diff --git a/web/source/lib/submit.js b/web/source/lib/submit.js deleted file mode 100644 index ae4108a01..000000000 --- a/web/source/lib/submit.js +++ /dev/null @@ -1,30 +0,0 @@ -/* -   GoToSocial -   Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org - -   This program is free software: you can redistribute it and/or modify -   it under the terms of the GNU Affero General Public License as published by -   the Free Software Foundation, either version 3 of the License, or -   (at your option) any later version. - -   This program is distributed in the hope that it will be useful, -   but WITHOUT ANY WARRANTY; without even the implied warranty of -   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -   GNU Affero General Public License for more details. - -   You should have received a copy of the GNU Affero General Public License -   along with this program.  If not, see <http://www.gnu.org/licenses/>. -*/ - -"use strict"; - -const React = require("react"); - -module.exports = function Submit({onClick, label, errorMsg, statusMsg}) { -	return ( -		<div className="messagebutton"> -			<button type="submit" onClick={onClick}>{ label }</button> -			<div className="error accent">{errorMsg ? errorMsg : statusMsg}</div> -		</div> -	); -}; diff --git a/web/source/package.json b/web/source/package.json index 691b68183..6e8deba09 100644 --- a/web/source/package.json +++ b/web/source/package.json @@ -1,6 +1,6 @@  {    "name": "gotosocial-frontend", -  "version": "0.3.8", +  "version": "0.5.0",    "description": "GoToSocial frontend sources",    "main": "index.js",    "author": "f0x", @@ -9,18 +9,23 @@      "@babel/core": "^7.12.13",      "@babel/preset-env": "^7.12.13",      "@babel/preset-react": "^7.12.13", +    "@f0x52/budo-express": "^1.1.0", +    "@reduxjs/toolkit": "^1.8.5",      "autoprefixer": "^10.4.8",      "babelify": "^10.0.0",      "bluebird": "^3.7.2",      "browserify": "^17.0.0",      "browserlist": "^1.0.1", -    "budo-express": "^1.0.8", +    "create-error": "^0.3.1",      "css-extract": "^2.0.0", +    "default-value": "^1.0.0", +    "dotty": "^0.1.2",      "eslint-plugin-react": "^7.24.0",      "express": "^4.18.1",      "factor-bundle": "^2.5.0", -    "from2-string": "^1.1.0",      "icssify": "^2.0.0", +    "is-plain-object": "^5.0.0", +    "is-valid-domain": "^0.1.6",      "js-file-download": "^0.4.12",      "modern-normalize": "^1.1.0",      "photoswipe": "^5.3.0", @@ -31,11 +36,17 @@      "postcss-nested": "^5.0.6",      "postcss-scss": "^4.0.4",      "postcss-strip-inline-comments": "^0.1.5", -    "pretty-bytes": "^5.6.0", -    "react": "^17.0.1", -    "react-dom": "^17.0.1", -    "reactify": "^1.1.1", -    "uglifyify": "^5.0.2" +    "prettier-bytes": "^1.0.4", +    "pretty-bytes": "4", +    "react": "18", +    "react-dom": "18", +    "react-error-boundary": "^3.1.4", +    "react-redux": "^8.0.2", +    "redux-devtools-extension": "^2.13.9", +    "redux-persist": "^6.0.0", +    "redux-thunk": "^2.4.1", +    "uglifyify": "^5.0.2", +    "wouter": "^2.8.0-alpha.2"    },    "devDependencies": {      "@f0x52/eslint-config-react": "^1.1.0", diff --git a/web/source/panels/admin/README.md b/web/source/panels/admin/README.md deleted file mode 100644 index 9a4572270..000000000 --- a/web/source/panels/admin/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# GoToSocial Admin Panel - -Standalone web admin panel for [GoToSocial](https://github.com/superseriousbusiness/gotosocial). - -A public hosted instance is also available at https://gts.superseriousbusiness.org/admin/, so you can fill your own instance URL in there. - -## Installation -Build requirements: some version of Node.js with npm, -``` -git clone https://github.com/superseriousbusiness/gotosocial-admin.git && cd gotosocial-admin -npm install -node index.js -``` -All processed build output will now be in `public/`, which you can copy over to a folder in your GoToSocial installation like `web/assets/admin`, or serve elsewhere. -No further configuration is required, authentication happens through normal OAUTH flow. - -## Development -Follow the installation steps, but run `NODE_ENV=development node index.js` to start the livereloading dev server instead. - -## License, donations -[AGPL-3.0](https://www.gnu.org/licenses/agpl-3.0.html). If you want to support my work, you can: <a href="https://liberapay.com/f0x/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a>
\ No newline at end of file diff --git a/web/source/panels/admin/blocks.js b/web/source/panels/admin/blocks.js deleted file mode 100644 index b12eb50a9..000000000 --- a/web/source/panels/admin/blocks.js +++ /dev/null @@ -1,318 +0,0 @@ -/* -   GoToSocial -   Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org - -   This program is free software: you can redistribute it and/or modify -   it under the terms of the GNU Affero General Public License as published by -   the Free Software Foundation, either version 3 of the License, or -   (at your option) any later version. - -   This program is distributed in the hope that it will be useful, -   but WITHOUT ANY WARRANTY; without even the implied warranty of -   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -   GNU Affero General Public License for more details. - -   You should have received a copy of the GNU Affero General Public License -   along with this program.  If not, see <http://www.gnu.org/licenses/>. -*/ - -"use strict"; - -const Promise = require("bluebird"); -const React = require("react"); -const fileDownload = require("js-file-download"); - -function sortBlocks(blocks) { -	return blocks.sort((a, b) => { // alphabetical sort -		return a.domain.localeCompare(b.domain); -	}); -} - -function deduplicateBlocks(blocks) { -	let a = new Map(); -	blocks.forEach((block) => { -		a.set(block.id, block); -	}); -	return Array.from(a.values()); -} - -module.exports = function Blocks({oauth}) { -	const [blocks, setBlocks] = React.useState([]); -	const [info, setInfo] = React.useState("Fetching blocks"); -	const [errorMsg, setError] = React.useState(""); -	const [checked, setChecked] = React.useState(new Set()); - -	React.useEffect(() => { -		Promise.try(() => { -			return oauth.apiRequest("/api/v1/admin/domain_blocks", undefined, undefined, "GET"); -		}).then((json) => { -			setInfo(""); -			setError(""); -			setBlocks(sortBlocks(json)); -		}).catch((e) => { -			setError(e.message); -			setInfo(""); -		}); -	}, []); - -	let blockList = blocks.map((block) => { -		function update(e) { -			let newChecked = new Set(checked.values()); -			if (e.target.checked) { -				newChecked.add(block.id); -			} else { -				newChecked.delete(block.id); -			} -			setChecked(newChecked); -		} - -		return ( -			<React.Fragment key={block.id}> -				<div><input type="checkbox" onChange={update} checked={checked.has(block.id)}></input></div> -				<div>{block.domain}</div> -				<div>{(new Date(block.created_at)).toLocaleString()}</div> -			</React.Fragment> -		); -	}); - -	function clearChecked() { -		setChecked(new Set()); -	} - -	function undoChecked() { -		let amount = checked.size; -		if(confirm(`Are you sure you want to remove ${amount} block(s)?`)) { -			setInfo(""); -			Promise.map(Array.from(checked.values()), (block) => { -				console.log("deleting", block); -				return oauth.apiRequest(`/api/v1/admin/domain_blocks/${block}`, "DELETE"); -			}).then((res) => { -				console.log(res); -				setInfo(`Deleted ${amount} blocks: ${res.map((a) => a.domain).join(", ")}`); -			}).catch((e) => { -				setError(e); -			}); - -			let newBlocks = blocks.filter((block) => { -				if (checked.size > 0 && checked.has(block.id)) { -					checked.delete(block.id); -					return false; -				} else { -					return true; -				} -			}); -			setBlocks(newBlocks); -			clearChecked(); -		} -	} - -	return ( -		<section className="blocks"> -			<h1>Blocks</h1> -			<div className="error accent">{errorMsg}</div> -			<div>{info}</div> -			<AddBlock oauth={oauth} blocks={blocks} setBlocks={setBlocks} /> -			<h3>Blocks:</h3> -			<div style={{display: "grid", gridTemplateColumns: "1fr auto"}}> -				<span onClick={clearChecked} className="accent" style={{alignSelf: "end"}}>uncheck all</span> -				<button onClick={undoChecked}>Unblock selected</button> -			</div> -			<div className="blocklist overflow"> -				{blockList} -			</div> -			<BulkBlocking oauth={oauth} blocks={blocks} setBlocks={setBlocks}/> -		</section> -	); -}; - -function BulkBlocking({oauth, blocks, setBlocks}) { -	const [bulk, setBulk] = React.useState(""); -	const [blockMap, setBlockMap] = React.useState(new Map()); -	const [output, setOutput] = React.useState(); - -	React.useEffect(() => { -		let newBlockMap = new Map(); -		blocks.forEach((block) => { -			newBlockMap.set(block.domain, block); -		}); -		setBlockMap(newBlockMap); -	}, [blocks]); - -	const fileRef = React.useRef(); - -	function error(e) { -		setOutput(<div className="error accent">{e}</div>); -		throw e; -	} - -	function fileUpload() { -		let reader = new FileReader(); -		reader.addEventListener("load", (e) => { -			try { -				// TODO: use validatem? -				let json = JSON.parse(e.target.result); -				json.forEach((block) => { -					console.log("block:", block); -				}); -			} catch(e) { -				error(e.message); -			} -		}); -		reader.readAsText(fileRef.current.files[0]); -	} - -	React.useEffect(() => { -		if (fileRef && fileRef.current) { -			fileRef.current.addEventListener("change", fileUpload); -		} -		return function cleanup() { -			fileRef.current.removeEventListener("change", fileUpload); -		}; -	}); - -	function textImport() { -		Promise.try(() => { -			if (bulk[0] == "[") { -				// assume it's json -				return JSON.parse(bulk); -			} else { -				return bulk.split("\n").map((val) => { -					return { -						domain: val.trim() -					}; -				}); -			} -		}).then((domains) => { -			console.log(domains); -			let before = domains.length; -			setOutput(`Importing ${before} domain(s)`); -			domains = domains.filter(({domain}) => { -				return (domain != "" && !blockMap.has(domain)); -			}); -			setOutput(<span>{output}<br/>{`Deduplicated ${before - domains.length}/${before} with existing blocks, adding ${domains.length} block(s)`}</span>); -			if (domains.length > 0) { -				let data = new FormData(); -				data.append("domains", new Blob([JSON.stringify(domains)], {type: "application/json"}), "import.json"); -				return oauth.apiRequest("/api/v1/admin/domain_blocks?import=true", "POST", data, "form"); -			} -		}).then((json) => { -			console.log("bulk import result:", json); -			setBlocks(sortBlocks(deduplicateBlocks([...json, ...blocks]))); -		}).catch((e) => { -			error(e.message); -		}); -	} - -	function textExport() { -		setBulk(blocks.reduce((str, val) => { -			if (typeof str == "object") { -				return str.domain; -			} else { -				return str + "\n" + val.domain; -			} -		})); -	} - -	function jsonExport() { -		Promise.try(() => { -			return oauth.apiRequest("/api/v1/admin/domain_blocks?export=true", "GET"); -		}).then((json) => { -			fileDownload(JSON.stringify(json), "block-export.json"); -		}).catch((e) => { -			error(e); -		}); -	} - -	function textAreaUpdate(e) { -		setBulk(e.target.value); -	} - -	return ( -		<React.Fragment> -			<h3>Bulk import/export</h3> -			<label htmlFor="bulk">Domains, one per line:</label> -			<textarea value={bulk} rows={20} onChange={textAreaUpdate}></textarea> -			<div className="controls"> -				<button onClick={textImport}>Import All From Field</button> -				<button onClick={textExport}>Export To Field</button> -				<label className="button" htmlFor="upload">Upload .json</label> -				<button onClick={jsonExport}>Download .json</button> -			</div> -			{output} -			<input type="file" id="upload" className="hidden" ref={fileRef}></input> -		</React.Fragment> -	); -} - -function AddBlock({oauth, blocks, setBlocks}) { -	const [domain, setDomain] = React.useState(""); -	const [type, setType] = React.useState("suspend"); -	const [obfuscated, setObfuscated] = React.useState(false); -	const [privateDescription, setPrivateDescription] = React.useState(""); -	const [publicDescription, setPublicDescription] = React.useState(""); - -	function addBlock() { -		console.log(`${type}ing`, domain); -		Promise.try(() => { -			return oauth.apiRequest("/api/v1/admin/domain_blocks", "POST", { -				domain: domain, -				obfuscate: obfuscated, -				private_comment: privateDescription, -				public_comment: publicDescription -			}, "json"); -		}).then((json) => { -			setDomain(""); -			setPrivateDescription(""); -			setPublicDescription(""); -			setBlocks([json, ...blocks]); -		}); -	} - -	function onDomainChange(e) { -		setDomain(e.target.value); -	} - -	function onTypeChange(e) { -		setType(e.target.value); -	} - -	function onKeyDown(e) { -		if (e.key == "Enter") { -			addBlock(); -		} -	} - -	return ( -		<React.Fragment> -			<h3>Add Block:</h3> -			<div className="addblock"> -				<input id="domain" placeholder="instance" onChange={onDomainChange} value={domain} onKeyDown={onKeyDown} /> -				<select value={type} onChange={onTypeChange}> -					<option id="suspend">Suspend</option> -					<option id="silence">Silence</option> -				</select> -				<button onClick={addBlock}>Add</button> -				<div> -					<label htmlFor="private">Private description:</label><br/> -					<textarea id="private" value={privateDescription} onChange={(e) => setPrivateDescription(e.target.value)}></textarea> -				</div> -				<div> -					<label htmlFor="public">Public description:</label><br/> -					<textarea id="public" value={publicDescription} onChange={(e) => setPublicDescription(e.target.value)}></textarea> -				</div> -				<div className="single"> -					<label htmlFor="obfuscate">Obfuscate:</label> -					<input id="obfuscate" type="checkbox" value={obfuscated} onChange={(e) => setObfuscated(e.target.checked)}/> -				</div> -			</div> -		</React.Fragment> -	); -} - -// function Blocklist() { -// 	return ( -// 		<section className="blocklists"> -// 			<h1>Blocklists</h1> -// 		</section> -// 	); -// }
\ No newline at end of file diff --git a/web/source/panels/admin/index.js b/web/source/panels/admin/index.js deleted file mode 100644 index 0fc1601eb..000000000 --- a/web/source/panels/admin/index.js +++ /dev/null @@ -1,64 +0,0 @@ -/* -   GoToSocial -   Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org - -   This program is free software: you can redistribute it and/or modify -   it under the terms of the GNU Affero General Public License as published by -   the Free Software Foundation, either version 3 of the License, or -   (at your option) any later version. - -   This program is distributed in the hope that it will be useful, -   but WITHOUT ANY WARRANTY; without even the implied warranty of -   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -   GNU Affero General Public License for more details. - -   You should have received a copy of the GNU Affero General Public License -   along with this program.  If not, see <http://www.gnu.org/licenses/>. -*/ - -"use strict"; - -const Promise = require("bluebird"); -const React = require("react"); -const ReactDom = require("react-dom"); - -const createPanel = require("../lib/panel"); - -const Settings = require("./settings"); -const Blocks = require("./blocks"); - -require("../base.css"); -require("./style.css"); - -function AdminPanel({oauth}) { -	/*  -		Features: (issue #78) -		- [ ] Instance information updating -			  GET /api/v1/instance PATCH /api/v1/instance -		- [ ] Domain block creation, viewing, and deletion -			  GET /api/v1/admin/domain_blocks -			  POST /api/v1/admin/domain_blocks -			  GET /api/v1/admin/domain_blocks/DOMAIN_BLOCK_ID, DELETE /api/v1/admin/domain_blocks/DOMAIN_BLOCK_ID -		- [ ] Blocklist import/export -			  GET /api/v1/admin/domain_blocks?export=true -			  POST json file as form field domains to /api/v1/admin/domain_blocks -	*/ - -	return ( -		<React.Fragment> -			<Logout oauth={oauth}/> -			<Settings oauth={oauth} /> -			<Blocks oauth={oauth}/> -		</React.Fragment> -	); -} - -function Logout({oauth}) { -	return ( -		<div> -			<button onClick={oauth.logout}>Logout</button> -		</div> -	); -} - -createPanel("GoToSocial Admin Panel", ["admin"], AdminPanel);
\ No newline at end of file diff --git a/web/source/panels/admin/settings.js b/web/source/panels/admin/settings.js deleted file mode 100644 index c9f470464..000000000 --- a/web/source/panels/admin/settings.js +++ /dev/null @@ -1,182 +0,0 @@ -/* -   GoToSocial -   Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org - -   This program is free software: you can redistribute it and/or modify -   it under the terms of the GNU Affero General Public License as published by -   the Free Software Foundation, either version 3 of the License, or -   (at your option) any later version. - -   This program is distributed in the hope that it will be useful, -   but WITHOUT ANY WARRANTY; without even the implied warranty of -   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -   GNU Affero General Public License for more details. - -   You should have received a copy of the GNU Affero General Public License -   along with this program.  If not, see <http://www.gnu.org/licenses/>. -*/ - -"use strict"; - -const Promise = require("bluebird"); -const React = require("react"); - -module.exports = function Settings({oauth}) { -	const [info, setInfo] = React.useState({}); -	const [errorMsg, setError] = React.useState(""); -	const [statusMsg, setStatus] = React.useState("Fetching instance info"); - -	React.useEffect(() => { -		Promise.try(() => { -			return oauth.apiRequest("/api/v1/instance", "GET"); -		}).then((json) => { -			setInfo(json); -		}).catch((e) => { -			setError(e.message); -			setStatus(""); -		}); -	}, []); - -	function submit() { -		setStatus("PATCHing"); -		setError(""); -		return Promise.try(() => { -			let formDataInfo = new FormData(); -			Object.entries(info).forEach(([key, val]) => { -				if (key == "contact_account") { -					key = "contact_username"; -					val = val.username; -				} -				if (key == "email") { -					key = "contact_email"; -				} -				if (typeof val != "object") { -					formDataInfo.append(key, val); -				} -			}); -			return oauth.apiRequest("/api/v1/instance", "PATCH", formDataInfo, "form"); -		}).then((json) => { -			setStatus("Config saved"); -			console.log(json); -		}).catch((e) => { -			setError(e.message); -			setStatus(""); -		}); -	} - -	return ( -		<section className="info login"> -			<h1>Instance Information <button onClick={submit}>Save</button></h1> -			<div className="error accent"> -				{errorMsg} -			</div> -			<div> -				{statusMsg} -			</div> -			<form onSubmit={(e) => e.preventDefault()}> -				{editableObject(info)} -			</form> -		</section> -	); -}; - -function editableObject(obj, path=[]) { -	const readOnlyKeys = ["uri", "version", "urls_streaming_api", "stats"]; -	const hiddenKeys = ["contact_account_", "urls"]; -	const explicitShownKeys = ["contact_account_username"]; -	const implementedKeys = "title, contact_account_username, email, short_description, description, terms, avatar, header".split(", "); -	const textareaKeys = ["short_description", "description"] - -	let listing = Object.entries(obj).map(([key, val]) => { -		let fullkey = [...path, key].join("_"); - -		if ( -			hiddenKeys.includes(fullkey) || -			hiddenKeys.includes(path.join("_")+"_") // also match just parent path -		) { -			if (!explicitShownKeys.includes(fullkey)) { -				return null; -			} -		} - -		if (Array.isArray(val)) { -			// FIXME: handle this -		} else if (typeof val == "object") { -			return (<React.Fragment key={fullkey}> -				{editableObject(val, [...path, key])} -			</React.Fragment>); -		}  - -		let isImplemented = ""; -		if (!implementedKeys.includes(fullkey)) { -			isImplemented = " notImplemented"; -		} - -		let isReadOnly = ( -			readOnlyKeys.includes(fullkey) || -			readOnlyKeys.includes(path.join("_")) || -			isImplemented != "" -		); - -		let label = key.replace(/_/g, " "); -		if (path.length > 0) { -			label = `\u00A0`.repeat(4 * path.length) + label; -		} - -		let inputProps; -		let changeFunc; -		if (val === true || val === false) { -			inputProps = { -				type: "checkbox", -				defaultChecked: val, -				disabled: isReadOnly -			}; -			changeFunc = (e) => e.target.checked; -		} else if (val.length != 0 && !isNaN(val)) { -			inputProps = { -				type: "number", -				defaultValue: val, -				readOnly: isReadOnly -			}; -			changeFunc = (e) => e.target.value; -		} else { -			inputProps = { -				type: "text", -				defaultValue: val, -				readOnly: isReadOnly -			}; -			changeFunc = (e) => e.target.value; -		} - -		function setRef(element) { -			if (element != null) { -				element.addEventListener("change", (e) => { -					obj[key] = changeFunc(e); -				}); -			} -		} - -		let field; -		if (textareaKeys.includes(fullkey)) { -			field = <textarea className={isImplemented} ref={setRef} {...inputProps}></textarea> -		} else { -			field = <input className={isImplemented} ref={setRef} {...inputProps} /> -		} -		return ( -			<React.Fragment key={fullkey}> -				<label htmlFor={key} className="capitalize">{label}</label> -				<div className={isImplemented}> -					{field} -				</div> -			</React.Fragment> -		); -	}); -	return ( -		<React.Fragment> -			{path != "" && -				<><b>{path}:</b> <span id="filler"></span></> -			} -			{listing} -		</React.Fragment> -	); -}
\ No newline at end of file diff --git a/web/source/panels/admin/style.css b/web/source/panels/admin/style.css deleted file mode 100644 index 01195437f..000000000 --- a/web/source/panels/admin/style.css +++ /dev/null @@ -1,106 +0,0 @@ -/* -   GoToSocial -   Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org - -   This program is free software: you can redistribute it and/or modify -   it under the terms of the GNU Affero General Public License as published by -   the Free Software Foundation, either version 3 of the License, or -   (at your option) any later version. - -   This program is distributed in the hope that it will be useful, -   but WITHOUT ANY WARRANTY; without even the implied warranty of -   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -   GNU Affero General Public License for more details. - -   You should have received a copy of the GNU Affero General Public License -   along with this program.  If not, see <http://www.gnu.org/licenses/>. -*/ - -section.info { -	form { -		grid-template-columns: auto 1fr; -		width: calc(100% - 0.35rem); - -		input { -			width: 100%; -			line-height: 1.5rem; -		} - -		label, input { -			padding: 0.2rem 0.5rem; -		} - -		input[type=checkbox] { -			justify-self: start; -			width: initial; -		} - -		input:read-only { -			border: none; -		} - -		input:invalid { -			border-color: red; -		} -	} - -	textarea { -		width: 100%; -		height: 8rem; -	} - -	h1 { -		display: flex; -		justify-content: space-between; -		margin-bottom: 0.5rem; -	} -} - -section.blocks { -	.overflow { -		max-height: 80vh; -		overflow-y: auto; -	} - -	.blocklist { -		display: grid; -		grid-template-columns: auto 1fr auto; -		grid-gap: 0.35rem 0; -		 -		div { -			background: rgb(70, 79, 88); -			padding: 0.2rem 0.4rem; -		} -	} - -	.addblock { -		display: grid; -		grid-template-columns: 1fr auto auto; -		grid-gap: 0.35rem; - -		input, select { -			font-size: 1.2rem; -		} - -		input, select, textarea { -			padding: 0.5rem; -		} - -		div { -			grid-column: 1/4; -		} - -		div.single input { -			width: initial; -		} -	} - -	h3 { -		margin-bottom: 0; -	} - -	.controls { -		display: flex; -		gap: 0.5rem; -	} -} diff --git a/web/source/panels/base.css b/web/source/panels/base.css deleted file mode 100644 index 2d76ed080..000000000 --- a/web/source/panels/base.css +++ /dev/null @@ -1,67 +0,0 @@ -/* -   GoToSocial -   Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org - -   This program is free software: you can redistribute it and/or modify -   it under the terms of the GNU Affero General Public License as published by -   the Free Software Foundation, either version 3 of the License, or -   (at your option) any later version. - -   This program is distributed in the hope that it will be useful, -   but WITHOUT ANY WARRANTY; without even the implied warranty of -   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -   GNU Affero General Public License for more details. - -   You should have received a copy of the GNU Affero General Public License -   along with this program.  If not, see <http://www.gnu.org/licenses/>. -*/ - -body { -	grid-template-rows: auto 1fr; -} - -.capitalize { -	text-transform: capitalize; -} - -section { -	margin-bottom: 1rem; -} - -input, select, textarea { -	box-sizing: border-box; -} - -.error { -	font-weight: bold; -} - -.hidden { -	display: none; -} - -.messagebutton { -	margin-top: 1rem; -	display: flex; -	gap: 1rem; -	align-items: center; - -	button { -		white-space: nowrap; -	} -} - -.notImplemented { -	border: 2px solid rgb(70, 79, 88); -	background: repeating-linear-gradient( -		-45deg, -		#525c66, -		#525c66 10px, -		rgb(70, 79, 88) 10px, -		rgb(70, 79, 88) 20px -	) !important; -} - -.mono { -	font-family: monospace; -} diff --git a/web/source/panels/lib/oauth.js b/web/source/panels/lib/oauth.js deleted file mode 100644 index 3619dfa01..000000000 --- a/web/source/panels/lib/oauth.js +++ /dev/null @@ -1,227 +0,0 @@ -/* -   GoToSocial -   Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org - -   This program is free software: you can redistribute it and/or modify -   it under the terms of the GNU Affero General Public License as published by -   the Free Software Foundation, either version 3 of the License, or -   (at your option) any later version. - -   This program is distributed in the hope that it will be useful, -   but WITHOUT ANY WARRANTY; without even the implied warranty of -   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -   GNU Affero General Public License for more details. - -   You should have received a copy of the GNU Affero General Public License -   along with this program.  If not, see <http://www.gnu.org/licenses/>. -*/ - -"use strict"; - -const Promise = require("bluebird"); - -function getCurrentUrl() { -	return window.location.origin + window.location.pathname; // strips ?query=string and #hash -} - -module.exports = function oauthClient(config, initState) { -	/* config:  -		instance: instance domain (https://testingtesting123.xyz) -		client_name: "GoToSocial Admin Panel" -		scope: [] -		website:  -	*/ - -	let state = initState; -	if (initState == undefined) { -		state = localStorage.getItem("oauth"); -		if (state == undefined) { -			state = { -				config -			}; -			storeState(); -		} else { -			state = JSON.parse(state); -		} -	} - -	function storeState() { -		localStorage.setItem("oauth", JSON.stringify(state)); -	} - -	/* register app -		/api/v1/apps -	*/ -	function register() { -		if (state.client_id != undefined) { -			return true; // we already have a registration -		} -		let url = new URL(config.instance); -		url.pathname = "/api/v1/apps"; - -		return fetch(url.href, { -			method: "POST", -			headers: { -				'Content-Type': 'application/json' -			}, -			body: JSON.stringify({ -				client_name: config.client_name, -				redirect_uris: getCurrentUrl(), -				scopes: config.scope.join(" "), -				website: getCurrentUrl() -			}) -		}).then((res) => { -			if (res.status != 200) { -				throw res; -			} -			return res.json(); -		}).then((json) => { -			state.client_id = json.client_id; -			state.client_secret = json.client_secret; -			storeState(); -		}); -	} -	 -	/* authorize: -		/oauth/authorize -			?client_id=CLIENT_ID -			&redirect_uri=window.location.href -			&response_type=code -			&scope=admin -	*/ -	function authorize() { -		let url = new URL(config.instance); -		url.pathname = "/oauth/authorize"; -		url.searchParams.set("client_id", state.client_id); -		url.searchParams.set("redirect_uri", getCurrentUrl()); -		url.searchParams.set("response_type", "code"); -		url.searchParams.set("scope", config.scope.join(" ")); - -		window.location.assign(url.href); -	} -	 -	function callback() { -		if (state.access_token != undefined) { -			return; // we're already done :) -		} -		let params = (new URL(window.location)).searchParams; -	 -		let token = params.get("code"); -		if (token != null) { -			console.log("got token callback:", token); -		} - -		return authorizeToken(token) -			.catch((e) => { -				console.log("Error processing oauth callback:", e); -				logout(); // just to be sure -			}); -	} - -	function authorizeToken(token) { -		let url = new URL(config.instance); -		url.pathname = "/oauth/token"; -		return fetch(url.href, { -			method: "POST", -			headers: { -				"Content-Type": "application/json" -			}, -			body: JSON.stringify({ -				client_id: state.client_id, -				client_secret: state.client_secret, -				redirect_uri: getCurrentUrl(), -				grant_type: "authorization_code", -				code: token -			}) -		}).then((res) => { -			if (res.status != 200) { -				throw res; -			} -			return res.json(); -		}).then((json) => { -			state.access_token = json.access_token; -			storeState(); -			window.location = getCurrentUrl(); // clear ?token= -		}); -	} - -	function isAuthorized() { -		return (state.access_token != undefined); -	} - -	function apiRequest(path, method, data, type="json", accept="json") { -		if (!isAuthorized()) { -			throw new Error("Not Authenticated"); -		} -		let url = new URL(config.instance); -		let [p, s] = path.split("?"); -		url.pathname = p; -		if (s != undefined) { -			url.search = s; -		} -		let headers = { -			"Authorization": `Bearer ${state.access_token}`, -			"Accept": accept == "json" ? "application/json" : "*/*" -		}; -		let body = data; -		if (type == "json" && body != undefined) { -			headers["Content-Type"] = "application/json"; -			body = JSON.stringify(data); -		} -		return fetch(url.href, { -			method, -			headers, -			body -		}).then((res) => { -			return Promise.all([res.json(), res]); -		}).then(([json, res]) => { -			if (res.status != 200) { -				if (json.error) { -					throw new Error(json.error); -				} else { -					throw new Error(`${res.status}: ${res.statusText}`); -				} -			} else { -				return json; -			} -		}).catch(e => { -			if (e instanceof SyntaxError) { -				throw new Error("Error: The GtS API returned a non-json error. This usually means a network problem, or an issue with your instance's reverse proxy configuration.", {cause: e}); -			} else { -				throw e; -			} -		}); -	} - -	function logout() { -		let url = new URL(config.instance); -		url.pathname = "/oauth/revoke"; -		return fetch(url.href, { -			method: "POST", -			headers: { -				"Content-Type": "application/json" -			}, -			body: JSON.stringify({ -				client_id: state.client_id, -				client_secret: state.client_secret, -				token: state.access_token, -			}) -		}).then((res) => { -			if (res.status != 200) { -				// GoToSocial doesn't actually implement this route yet, -				// so error is to be expected -				return; -			} -			return res.json(); -		}).catch(() => { -			// see above -		}).then(() => { -			localStorage.removeItem("oauth"); -			window.location = getCurrentUrl(); -		}); -	} - -	return { -		register, authorize, callback, isAuthorized, apiRequest, logout -	}; -}; diff --git a/web/source/panels/user/basic.js b/web/source/panels/user/basic.js deleted file mode 100644 index f507b782b..000000000 --- a/web/source/panels/user/basic.js +++ /dev/null @@ -1,151 +0,0 @@ -/* -   GoToSocial -   Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org - -   This program is free software: you can redistribute it and/or modify -   it under the terms of the GNU Affero General Public License as published by -   the Free Software Foundation, either version 3 of the License, or -   (at your option) any later version. - -   This program is distributed in the hope that it will be useful, -   but WITHOUT ANY WARRANTY; without even the implied warranty of -   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -   GNU Affero General Public License for more details. - -   You should have received a copy of the GNU Affero General Public License -   along with this program.  If not, see <http://www.gnu.org/licenses/>. -*/ - -"use strict"; - -const React = require("react"); -const Promise = require("bluebird"); - -const Submit = require("../../lib/submit"); - -module.exports = function Basic({oauth, account, allowCustomCSS}) { -	const [errorMsg, setError] = React.useState(""); -	const [statusMsg, setStatus] = React.useState(""); - -	const [headerFile, setHeaderFile] = React.useState(undefined); -	const [headerSrc, setHeaderSrc] = React.useState(""); - -	const [avatarFile, setAvatarFile] = React.useState(undefined); -	const [avatarSrc, setAvatarSrc] = React.useState(""); - -	const [displayName, setDisplayName] = React.useState(""); -	const [bio, setBio] = React.useState(""); -	const [locked, setLocked] = React.useState(false); -	const [customCSS, setCustomCSS] = React.useState(""); - -	React.useEffect(() => { -		setHeaderSrc(account.header); -		setAvatarSrc(account.avatar); - -		setDisplayName(account.display_name); -		setBio(account.source ? account.source.note : ""); -		setLocked(account.locked); -		setCustomCSS((allowCustomCSS && account.custom_css) ? account.custom_css : ""); -	}, [account, setHeaderSrc, setAvatarSrc, setDisplayName, setBio, setLocked, setCustomCSS]); - -	const headerOnChange = (e) => { -		setHeaderFile(e.target.files[0]); -		setHeaderSrc(URL.createObjectURL(e.target.files[0])); -	}; - -	const avatarOnChange = (e) => { -		setAvatarFile(e.target.files[0]); -		setAvatarSrc(URL.createObjectURL(e.target.files[0])); -	}; - -	const submit = (e) => { -		e.preventDefault(); - -		setStatus("PATCHing"); -		setError(""); -		return Promise.try(() => { -			let formDataInfo = new FormData(); - -			if (headerFile) { -				formDataInfo.set("header", headerFile); -			} - -			if (avatarFile) { -				formDataInfo.set("avatar", avatarFile); -			} - -			formDataInfo.set("display_name", displayName); -			formDataInfo.set("note", bio); -			formDataInfo.set("locked", locked); -			 -			if (allowCustomCSS) { -				formDataInfo.set("custom_css", customCSS); -			} -			 -			return oauth.apiRequest("/api/v1/accounts/update_credentials", "PATCH", formDataInfo, "form"); -		}).then((json) => { -			setStatus("Saved!"); - -			setHeaderSrc(json.header); -			setAvatarSrc(json.avatar); - -			setDisplayName(json.display_name); -			setBio(json.source.note); -			setLocked(json.locked); -			setCustomCSS(allowCustomCSS && json.custom_css ? json.custom_css : ""); -		}).catch((e) => { -			setError(e.message); -			setStatus(""); -		}); -	}; - -	return ( -		<section className="basic"> -			<h1>@{account.username}'s Profile Info</h1> -			<form> -				<div className="labelinput"> -					<label htmlFor="header">Header</label> -					<div className="border"> -						<img className="headerpreview" src={headerSrc} alt={headerSrc ? `header image for ${account.username}` : "None set"}/> -						<div> -							<label htmlFor="header" className="file-input button">Browse…</label> -							<span>{headerFile ? headerFile.name : ""}</span> -						</div> -					</div> -					<input className="hidden" id="header" type="file" accept="image/*" onChange={headerOnChange}/> -				</div> -				<div className="labelinput"> -					<label htmlFor="avatar">Avatar</label> -					<div className="border"> -						<img className="avatarpreview" src={avatarSrc} alt={headerSrc ? `avatar image for ${account.username}` : "None set"}/> -						<div> -							<label htmlFor="avatar" className="file-input button">Browse…</label> -							<span>{avatarFile ? avatarFile.name : ""}</span> -						</div> -					</div> -					<input className="hidden" id="avatar" type="file" accept="image/*" onChange={avatarOnChange}/> -				</div> -				<div className="labelinput"> -					<label htmlFor="displayname">Display Name</label> -					<input id="displayname" type="text" value={displayName} onChange={(e) => setDisplayName(e.target.value)} placeholder="A GoToSocial user"/> -				</div> -				<div className="labelinput"> -					<label htmlFor="bio">Bio</label> -					<textarea id="bio" value={bio} onChange={(e) => setBio(e.target.value)} placeholder="Just trying out GoToSocial, my pronouns are they/them and I like sloths."/> -				</div> -				{ !allowCustomCSS ? null :   -					<div className="labelinput"> -						<label htmlFor="customcss">Custom CSS</label> -						<textarea className="mono" id="customcss" value={customCSS} onChange={(e) => setCustomCSS(e.target.value)}/> -						<a href="https://docs.gotosocial.org/en/latest/user_guide/custom_css" target="_blank" className="moreinfolink" rel="noreferrer">Learn more about custom CSS (opens in a new tab)</a> -					</div> -				} -				<div className="labelcheckbox"> -					<label htmlFor="locked">Manually approve follow requests</label> -					<input id="locked" type="checkbox" checked={locked} onChange={(e) => setLocked(e.target.checked)}/> -				</div> -				<Submit onClick={submit} label="Save profile info" errorMsg={errorMsg} statusMsg={statusMsg}/> -			</form> -		</section> -	); -}; diff --git a/web/source/panels/user/index.js b/web/source/panels/user/index.js deleted file mode 100644 index aeecac415..000000000 --- a/web/source/panels/user/index.js +++ /dev/null @@ -1,76 +0,0 @@ -/* -   GoToSocial -   Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org - -   This program is free software: you can redistribute it and/or modify -   it under the terms of the GNU Affero General Public License as published by -   the Free Software Foundation, either version 3 of the License, or -   (at your option) any later version. - -   This program is distributed in the hope that it will be useful, -   but WITHOUT ANY WARRANTY; without even the implied warranty of -   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -   GNU Affero General Public License for more details. - -   You should have received a copy of the GNU Affero General Public License -   along with this program.  If not, see <http://www.gnu.org/licenses/>. -*/ - -"use strict"; - -const Promise = require("bluebird"); -const React = require("react"); -const ReactDom = require("react-dom"); - -const createPanel = require("../lib/panel"); - -const Basic = require("./basic"); -const Posts = require("./posts"); -const Security = require("./security"); - -require("../base.css"); -require("./style.css"); - -function UserPanel({oauth}) { -	const [account, setAccount] = React.useState({}); -	const [allowCustomCSS, setAllowCustomCSS] = React.useState(false); -	const [errorMsg, setError] = React.useState(""); -	const [statusMsg, setStatus] = React.useState("Fetching user info"); - -	React.useEffect(() => { - -	}, [oauth, setAllowCustomCSS, setError, setStatus]); - -	React.useEffect(() => { -		Promise.try(() => { -			return oauth.apiRequest("/api/v1/instance", "GET"); -		}).then((json) => { -			setAllowCustomCSS(json.configuration.accounts.allow_custom_css); -			Promise.try(() => { -				return oauth.apiRequest("/api/v1/accounts/verify_credentials", "GET"); -			}).then((json) => { -				setAccount(json); -			}).catch((e) => { -				setError(e.message); -				setStatus(""); -			}); -		}).catch((e) => { -			setError(e.message); -			setStatus(""); -		}); - -	}, [oauth, setAllowCustomCSS, setAccount, setError, setStatus]); - -	return ( -		<React.Fragment> -			<div> -				<button className="logout" onClick={oauth.logout}>Log out of settings panel</button> -			</div> -			<Basic oauth={oauth} account={account} allowCustomCSS={allowCustomCSS}/> -			<Posts oauth={oauth} account={account}/> -			<Security oauth={oauth}/> -		</React.Fragment> -	); -} - -createPanel("GoToSocial User Panel", ["read write"], UserPanel);
\ No newline at end of file diff --git a/web/source/panels/user/posts.js b/web/source/panels/user/posts.js deleted file mode 100644 index e4ceae617..000000000 --- a/web/source/panels/user/posts.js +++ /dev/null @@ -1,107 +0,0 @@ -/* -   GoToSocial -   Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org - -   This program is free software: you can redistribute it and/or modify -   it under the terms of the GNU Affero General Public License as published by -   the Free Software Foundation, either version 3 of the License, or -   (at your option) any later version. - -   This program is distributed in the hope that it will be useful, -   but WITHOUT ANY WARRANTY; without even the implied warranty of -   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -   GNU Affero General Public License for more details. - -   You should have received a copy of the GNU Affero General Public License -   along with this program.  If not, see <http://www.gnu.org/licenses/>. -*/ - -"use strict"; - -const React = require("react"); -const Promise = require("bluebird"); - -const Languages = require("./languages"); -const Submit = require("../../lib/submit"); - -module.exports = function Posts({oauth, account}) { -	const [errorMsg, setError] = React.useState(""); -	const [statusMsg, setStatus] = React.useState(""); - -	const [language, setLanguage] = React.useState(""); -	const [privacy, setPrivacy] = React.useState(""); -	const [format, setFormat] = React.useState(""); -	const [sensitive, setSensitive] = React.useState(false); - -	React.useEffect(() => { -		if (account.source) { -			setLanguage(account.source.language.toUpperCase()); -			setPrivacy(account.source.privacy); -			setSensitive(account.source.sensitive ? account.source.sensitive : false); -			setFormat(account.source.status_format ? account.source.status_format : "plain"); -		} -         -	}, [account, setSensitive, setPrivacy]); - -	const submit = (e) => { -		e.preventDefault(); - -		setStatus("PATCHing"); -		setError(""); -		return Promise.try(() => { -			let formDataInfo = new FormData(); - -			formDataInfo.set("source[language]", language); -			formDataInfo.set("source[privacy]", privacy); -			formDataInfo.set("source[sensitive]", sensitive); -			formDataInfo.set("source[status_format]", format); - -			return oauth.apiRequest("/api/v1/accounts/update_credentials", "PATCH", formDataInfo, "form"); -		}).then((json) => { -			setStatus("Saved!"); -			setLanguage(json.source.language.toUpperCase()); -			setPrivacy(json.source.privacy); -			setSensitive(json.source.sensitive ? json.source.sensitive : false); -			setFormat(json.source.status_format ? json.source.status_format : "plain"); -		}).catch((e) => { -			setError(e.message); -			setStatus(""); -		}); -	}; - -	return ( -		<section className="posts"> -			<h1>Post Settings</h1> -			<form> -				<div className="labelselect"> -					<label htmlFor="language">Default post language</label> -					<select id="language" autoComplete="language" value={language} onChange={(e) => setLanguage(e.target.value)}> -						<Languages /> -					</select> -				</div> -				<div className="labelselect"> -					<label htmlFor="privacy">Default post privacy</label> -					<select id="privacy" value={privacy} onChange={(e) => setPrivacy(e.target.value)}> -						<option value="private">Private / followers-only)</option> -						<option value="unlisted">Unlisted</option> -						<option value="public">Public</option> -					</select> -					<a href="https://docs.gotosocial.org/en/latest/user_guide/posts/#privacy-settings" target="_blank" className="moreinfolink" rel="noreferrer">Learn more about post privacy settings (opens in a new tab)</a> -				</div> -				<div className="labelselect"> -					<label htmlFor="format">Default post format</label> -					<select id="format" value={format} onChange={(e) => setFormat(e.target.value)}> -						<option value="plain">Plain (default)</option> -						<option value="markdown">Markdown</option> -					</select> -					<a href="https://docs.gotosocial.org/en/latest/user_guide/posts/#input-types" target="_blank" className="moreinfolink" rel="noreferrer">Learn more about post format settings (opens in a new tab)</a> -				</div>				 -				<div className="labelcheckbox"> -					<label htmlFor="sensitive">Mark my posts as sensitive by default</label> -					<input id="sensitive" type="checkbox" checked={sensitive} onChange={(e) => setSensitive(e.target.checked)}/> -				</div> -				<Submit onClick={submit} label="Save post settings" errorMsg={errorMsg} statusMsg={statusMsg}/> -			</form> -		</section> -	); -}; diff --git a/web/source/panels/user/security.js b/web/source/panels/user/security.js deleted file mode 100644 index f5925083d..000000000 --- a/web/source/panels/user/security.js +++ /dev/null @@ -1,80 +0,0 @@ -/* -   GoToSocial -   Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org - -   This program is free software: you can redistribute it and/or modify -   it under the terms of the GNU Affero General Public License as published by -   the Free Software Foundation, either version 3 of the License, or -   (at your option) any later version. - -   This program is distributed in the hope that it will be useful, -   but WITHOUT ANY WARRANTY; without even the implied warranty of -   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -   GNU Affero General Public License for more details. - -   You should have received a copy of the GNU Affero General Public License -   along with this program.  If not, see <http://www.gnu.org/licenses/>. -*/ - -"use strict"; - -const React = require("react"); -const Promise = require("bluebird"); - -const Submit = require("../../lib/submit"); - -module.exports = function Security({oauth}) { -	const [errorMsg, setError] = React.useState(""); -	const [statusMsg, setStatus] = React.useState(""); - -	const [oldPassword, setOldPassword] = React.useState(""); -	const [newPassword, setNewPassword] = React.useState(""); -	const [newPasswordConfirm, setNewPasswordConfirm] = React.useState(""); - -	const submit = (e) => { -		e.preventDefault(); - -		if (newPassword !== newPasswordConfirm) { -			setError("New password and confirm new password did not match!"); -			return; -		} -       -		setStatus("PATCHing"); -		setError(""); -		return Promise.try(() => { -			let formDataInfo = new FormData(); -			formDataInfo.set("old_password", oldPassword); -			formDataInfo.set("new_password", newPassword); -			return oauth.apiRequest("/api/v1/user/password_change", "POST", formDataInfo, "form"); -		}).then((json) => { -			setStatus("Saved!"); -			setOldPassword(""); -			setNewPassword(""); -			setNewPasswordConfirm(""); -		}).catch((e) => { -			setError(e.message); -			setStatus(""); -		}); -	}; - -	return ( -		<section className="security"> -			<h1>Password Change</h1> -			<form> -				<div className="labelinput"> -					<label htmlFor="password">Current password</label> -					<input name="password" id="password" type="password" autoComplete="current-password" value={oldPassword} onChange={(e) => setOldPassword(e.target.value)} /> -				</div> -				<div className="labelinput"> -					<label htmlFor="new-password">New password</label> -					<input name="new-password" id="new-password" type="password" autoComplete="new-password" value={newPassword} onChange={(e) => setNewPassword(e.target.value)} /> -				</div> -				<div className="labelinput"> -					<label htmlFor="confirm-new-password">Confirm new password</label> -					<input name="confirm-new-password" id="confirm-new-password" type="password" autoComplete="new-password" value={newPasswordConfirm} onChange={(e) => setNewPasswordConfirm(e.target.value)} /> -				</div> -				<Submit onClick={submit} label="Save new password" errorMsg={errorMsg} statusMsg={statusMsg}/> -			</form> -		</section> -	); -}; diff --git a/web/source/panels/user/style.css b/web/source/panels/user/style.css deleted file mode 100644 index 021b1816e..000000000 --- a/web/source/panels/user/style.css +++ /dev/null @@ -1,118 +0,0 @@ -/* -   GoToSocial -   Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org - -   This program is free software: you can redistribute it and/or modify -   it under the terms of the GNU Affero General Public License as published by -   the Free Software Foundation, either version 3 of the License, or -   (at your option) any later version. - -   This program is distributed in the hope that it will be useful, -   but WITHOUT ANY WARRANTY; without even the implied warranty of -   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -   GNU Affero General Public License for more details. - -   You should have received a copy of the GNU Affero General Public License -   along with this program.  If not, see <http://www.gnu.org/licenses/>. -*/ - -section.basic, section.posts, section.security { -	form { -		display: flex; -		flex-direction: column; -		gap: 1rem; - -		input, textarea { -			width: 100%; -			line-height: 1.5rem; -		} - -		input[type=checkbox] { -			justify-self: start; -			width: initial; -		} - -		input:read-only { -			border: none; -		} - -		input:invalid { -			border-color: red; -		} -	} - -	textarea { -		width: 100%; -		height: 8rem; -	} - -	h1 { -		margin-bottom: 0.5rem; -	} - -	img { -		display: flex; -		justify-content: center; -		align-items: center; -		border: $boxshadow_border; -		box-shadow: $box-shadow; -		object-fit: cover; -		border-radius: 0.2rem; -		box-sizing: border-box; -		margin-bottom: 0.5rem; -	} - -	.avatarpreview { -		height: 8.5rem; -		width: 8.5rem; -	} -	 -	.headerpreview { -		width: 100%; -		aspect-ratio: 3 / 1; -		overflow: hidden; -	} - -	.moreinfolink { -		font-size: 0.9em; -	} -} - -.labelinput .border { -	border-radius: 0.2rem; -	border: 0.15rem solid $border_accent; -	padding: 0.3rem; -	display: flex; -	flex-direction: column; -} - -.file-input.button { -	display: inline-block; -	font-size: 1rem; -	font-weight: normal; -	padding: 0.3rem 0.3rem; -	align-self: flex-start; -	/* background: $border_accent; */ -	margin-right: 0.2rem; -} - -.labelinput, .labelselect { -	display: flex; -	flex-direction: column; -	gap: 0.4rem; -} - -.labelcheckbox { -	display: flex; -	gap: 0.4rem; -} - -.titlesave { -	display: flex; -	flex-wrap: wrap; -	gap: 0.4rem; -} - -.logout { -	margin-bottom: 2rem; -} diff --git a/web/source/settings-panel/admin/actions.js b/web/source/settings-panel/admin/actions.js new file mode 100644 index 000000000..d4980d021 --- /dev/null +++ b/web/source/settings-panel/admin/actions.js @@ -0,0 +1,61 @@ +/* +	GoToSocial +	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU Affero General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. + +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU Affero General Public License for more details. + +	You should have received a copy of the GNU Affero General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const Promise = require("bluebird"); +const React = require("react"); +const Redux = require("react-redux"); + +const Submit = require("../components/submit"); + +const api = require("../lib/api"); +const submit = require("../lib/submit"); + +module.exports = function AdminActionPanel() { +	const dispatch = Redux.useDispatch(); + +	const [days, setDays] = React.useState(30); + +	const [errorMsg, setError] = React.useState(""); +	const [statusMsg, setStatus] = React.useState(""); + +	const removeMedia = submit( +		() => dispatch(api.admin.mediaCleanup(days)), +		{setStatus, setError} +	); + +	return ( +		<> +			<h1>Admin Actions</h1> +			<div> +				<h2>Media cleanup</h2> +				<p> +					Clean up remote media older than the specified number of days. +					If the remote instance is still online they will be refetched when needed. +					Also cleans up unused headers and avatars from the media cache. +				</p> +				<div> +					<label htmlFor="days">Days: </label> +					<input id="days" type="number" value={days} onChange={(e) => setDays(e.target.value)}/> +				</div> +				<Submit onClick={removeMedia} label="Remove media" errorMsg={errorMsg} statusMsg={statusMsg} /> +			</div> +		</> +	); +};
\ No newline at end of file diff --git a/web/source/settings-panel/admin/emoji.js b/web/source/settings-panel/admin/emoji.js new file mode 100644 index 000000000..1ef4a54a3 --- /dev/null +++ b/web/source/settings-panel/admin/emoji.js @@ -0,0 +1,212 @@ +/* +	GoToSocial +	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU Affero General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. + +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU Affero General Public License for more details. + +	You should have received a copy of the GNU Affero General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const Promise = require("bluebird"); +const React = require("react"); +const Redux = require("react-redux"); +const {Switch, Route, Link, Redirect, useRoute, useLocation} = require("wouter"); + +const Submit = require("../components/submit"); +const FakeToot = require("../components/fake-toot"); +const { formFields } = require("../components/form-fields"); + +const api = require("../lib/api"); +const adminActions = require("../redux/reducers/admin").actions; +const submit = require("../lib/submit"); + +const base = "/settings/admin/custom-emoji"; + +module.exports = function CustomEmoji() { +	return ( +		<Switch> +			<Route path={`${base}/:emojiId`}> +				<EmojiDetailWrapped /> +			</Route> +			<EmojiOverview /> +		</Switch> +	); +}; + +function EmojiOverview() { +	const dispatch = Redux.useDispatch(); +	const [loaded, setLoaded] = React.useState(false); + +	const [errorMsg, setError] = React.useState(""); + +	React.useEffect(() => { +		if (!loaded) { +			Promise.try(() => { +				return dispatch(api.admin.fetchCustomEmoji()); +			}).then(() => { +				setLoaded(true); +			}).catch((e) => { +				setLoaded(true); +				setError(e.message); +			}); +		} +	}, []); + +	if (!loaded) { +		return ( +			<> +				<h1>Custom Emoji</h1> +				Loading... +			</> +		); +	} + +	return ( +		<> +			<h1>Custom Emoji</h1> +			<EmojiList/> +			<NewEmoji/> +			{errorMsg.length > 0 &&  +				<div className="error accent">{errorMsg}</div> +			} +		</> +	); +} + +const NewEmojiForm = formFields(adminActions.updateNewEmojiVal, (state) => state.admin.newEmoji); +function NewEmoji() { +	const dispatch = Redux.useDispatch(); +	const newEmojiForm = Redux.useSelector((state) => state.admin.newEmoji); + +	const [errorMsg, setError] = React.useState(""); +	const [statusMsg, setStatus] = React.useState(""); + +	const uploadEmoji = submit( +		() => dispatch(api.admin.newEmoji()), +		{ +			setStatus, setError, +			onSuccess: function() { +				URL.revokeObjectURL(newEmojiForm.image); +				return Promise.all([ +					dispatch(adminActions.updateNewEmojiVal(["image", undefined])), +					dispatch(adminActions.updateNewEmojiVal(["imageFile", undefined])), +					dispatch(adminActions.updateNewEmojiVal(["shortcode", ""])), +				]); +			} +		} +	); + +	React.useEffect(() => { +		if (newEmojiForm.shortcode.length == 0) { +			if (newEmojiForm.imageFile != undefined) { +				let [name, ext] = newEmojiForm.imageFile.name.split("."); +				dispatch(adminActions.updateNewEmojiVal(["shortcode", name])); +			} +		} +	}); + +	let emojiOrShortcode = `:${newEmojiForm.shortcode}:`; + +	if (newEmojiForm.image != undefined) { +		emojiOrShortcode = <img +			className="emoji" +			src={newEmojiForm.image} +			title={`:${newEmojiForm.shortcode}:`} +			alt={newEmojiForm.shortcode} +		/>; +	} + +	return ( +		<div> +			<h2>Add new custom emoji</h2> + +			<FakeToot> +				Look at this new custom emoji {emojiOrShortcode} isn't it cool? +			</FakeToot> + +			<NewEmojiForm.File +				id="image" +				name="Image" +				fileType="image/png,image/gif" +				showSize={true} +				maxSize={50 * 1000} +			/> + +			<NewEmojiForm.TextInput +				id="shortcode" +				name="Shortcode (without : :), must be unique on the instance" +				placeHolder="blobcat" +			/> + +			<Submit onClick={uploadEmoji} label="Upload" errorMsg={errorMsg} statusMsg={statusMsg} /> +		</div> +	); +} + +function EmojiList() { +	const emoji = Redux.useSelector((state) => state.admin.emoji); + +	return ( +		<div> +			<h2>Overview</h2> +			<div className="list emoji-list"> +				{Object.entries(emoji).map(([category, entries]) => { +					return <EmojiCategory key={category} category={category} entries={entries}/>; +				})} +			</div> +		</div> +	); +} + +function EmojiCategory({category, entries}) { +	return ( +		<div className="entry"> +			<b>{category}</b> +			<div className="emoji-group"> +				{entries.map((e) => { +					return ( +						// <Link key={e.static_url} to={`${base}/${e.shortcode}`}> +						<Link key={e.static_url} to={`${base}`}> +							<a> +								<img src={e.static_url} alt={e.shortcode} title={`:${e.shortcode}:`}/> +							</a> +						</Link> +					); +				})} +			</div> +		</div> +	); +} + +function EmojiDetailWrapped() { +	/* We wrap the component to generate formFields with a setter depending on the domain +		 if formFields() is used inside the same component that is re-rendered with their state, +		 inputs get re-created on every change, causing them to lose focus, and bad performance +	*/ +	let [_match, {emojiId}] = useRoute(`${base}/:emojiId`); + +	function alterEmoji([key, val]) { +		return adminActions.updateDomainBlockVal([emojiId, key, val]); +	} + +	const fields = formFields(alterEmoji, (state) => state.admin.blockedInstances[emojiId]); + +	return <EmojiDetail id={emojiId} Form={fields} />; +} + +function EmojiDetail({id, Form}) { +	return ( +		"Not implemented yet" +	); +}
\ No newline at end of file diff --git a/web/source/settings-panel/admin/federation.js b/web/source/settings-panel/admin/federation.js new file mode 100644 index 000000000..7afc3c699 --- /dev/null +++ b/web/source/settings-panel/admin/federation.js @@ -0,0 +1,382 @@ +/* +	GoToSocial +	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU Affero General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. + +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU Affero General Public License for more details. + +	You should have received a copy of the GNU Affero General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const Promise = require("bluebird"); +const React = require("react"); +const Redux = require("react-redux"); +const {Switch, Route, Link, Redirect, useRoute, useLocation} = require("wouter"); +const fileDownload = require("js-file-download"); + +const { formFields } = require("../components/form-fields"); + +const api = require("../lib/api"); +const adminActions = require("../redux/reducers/admin").actions; +const submit = require("../lib/submit"); + +const base = "/settings/admin/federation"; + +// const { +// 	TextInput, +// 	TextArea, +// 	File +// } = require("../components/form-fields").formFields(adminActions.setAdminSettingsVal, (state) => state.instances.adminSettings); + +module.exports = function AdminSettings() { +	const dispatch = Redux.useDispatch(); +	// const instance = Redux.useSelector(state => state.instances.adminSettings); +	const loadedBlockedInstances = Redux.useSelector(state => state.admin.loadedBlockedInstances); + +	React.useEffect(() => { +		if (!loadedBlockedInstances ) { +			Promise.try(() => { +				return dispatch(api.admin.fetchDomainBlocks()); +			}); +		} +	}, []); + +	if (!loadedBlockedInstances) { +		return ( +			<div> +				<h1>Federation</h1> +				Loading... +			</div> +		); +	} + +	return ( +		<Switch> +			<Route path={`${base}/:domain`}> +				<InstancePageWrapped /> +			</Route> +			<InstanceOverview /> +		</Switch> +	); +}; + +function InstanceOverview() { +	const [filter, setFilter] = React.useState(""); +	const blockedInstances = Redux.useSelector(state => state.admin.blockedInstances); +	const [_location, setLocation] = useLocation(); + +	function filterFormSubmit(e) { +		e.preventDefault(); +		setLocation(`${base}/${filter}`); +	} + +	return ( +		<> +			<h1>Federation</h1> +			Here you can see an overview of blocked instances. + +			<div className="instance-list"> +				<h2>Blocked instances</h2> +				<form action={`${base}/view`} className="filter" role="search" onSubmit={filterFormSubmit}> +					<input name="domain" value={filter} onChange={(e) => setFilter(e.target.value)}/> +					<Link to={`${base}/${filter}`}><a className="button">Add block</a></Link> +				</form> +				<div className="list"> +					{Object.values(blockedInstances).filter((a) => a.domain.startsWith(filter)).map((entry) => { +						return ( +							<Link key={entry.domain} to={`${base}/${entry.domain}`}> +								<a className="entry nounderline"> +									<span id="domain"> +										{entry.domain} +									</span> +									<span id="date"> +										{new Date(entry.created_at).toLocaleString()} +									</span> +								</a> +							</Link> +						); +					})} +				</div> +			</div> + +			<BulkBlocking/> +		</> +	); +} + +const Bulk = formFields(adminActions.updateBulkBlockVal, (state) => state.admin.bulkBlock); +function BulkBlocking() { +	const dispatch = Redux.useDispatch(); +	const {bulkBlock, blockedInstances} = Redux.useSelector(state => state.admin); + +	const [errorMsg, setError] = React.useState(""); +	const [statusMsg, setStatus] = React.useState(""); + +	function importBlocks() { +		setStatus("Processing"); +		setError(""); +		return Promise.try(() => { +			return dispatch(api.admin.bulkDomainBlock()); +		}).then(({success, invalidDomains}) => { +			return Promise.try(() => { +				return resetBulk(); +			}).then(() => { +				dispatch(adminActions.updateBulkBlockVal(["list", invalidDomains.join("\n")])); + +				let stat = ""; +				if (success == 0) { +					return setError("No valid domains in import"); +				} else if (success == 1) { +					stat = "Imported 1 domain"; +				} else { +					stat = `Imported ${success} domains`; +				} + +				if (invalidDomains.length > 0) { +					if (invalidDomains.length == 1) { +						stat += ", input contained 1 invalid domain."; +					} else { +						stat += `, input contained ${invalidDomains.length} invalid domains.`; +					} +				} else { +					stat += "!"; +				} + +				setStatus(stat); +			}); +		}).catch((e) => { +			console.error(e); +			setError(e.message); +			setStatus(""); +		}); +	} + +	function exportBlocks() { +		return Promise.try(() => { +			setStatus("Exporting"); +			setError(""); +			let asJSON = bulkBlock.exportType.startsWith("json"); +			let _asCSV = bulkBlock.exportType.startsWith("csv"); + +			let exportList = Object.values(blockedInstances).map((entry) => { +				if (asJSON) { +					return { +						domain: entry.domain, +						public_comment: entry.public_comment +					}; +				} else { +					return entry.domain; +				} +			}); +			 +			if (bulkBlock.exportType == "json") { +				return dispatch(adminActions.updateBulkBlockVal(["list", JSON.stringify(exportList)])); +			} else if (bulkBlock.exportType == "json-download") { +				return fileDownload(JSON.stringify(exportList), "block-export.json"); +			} else if (bulkBlock.exportType == "plain") { +				return dispatch(adminActions.updateBulkBlockVal(["list", exportList.join("\n")])); +			} +		}).then(() => { +			setStatus("Exported!"); +		}).catch((e) => { +			setError(e.message); +			setStatus(""); +		}); +	} + +	function resetBulk(e) { +		if (e != undefined) { +			e.preventDefault(); +		} +		return dispatch(adminActions.resetBulkBlockVal()); +	} + +	function disableInfoFields(props={}) { +		if (bulkBlock.list[0] == "[") { +			return { +				...props, +				disabled: true, +				placeHolder: "Domain list is a JSON import, input disabled" +			}; +		} else { +			return props; +		} +	} + +	return ( +		<div className="bulk"> +			<h2>Import / Export <a onClick={resetBulk}>reset</a></h2> +			<Bulk.TextArea +				id="list" +				name="Domains, one per line" +				placeHolder={`google.com\nfacebook.com`} +			/> + +			<Bulk.TextArea +				id="public_comment" +				name="Public comment" +				inputProps={disableInfoFields({rows: 3})} +			/> + +			<Bulk.TextArea +				id="private_comment" +				name="Private comment" +				inputProps={disableInfoFields({rows: 3})} +			/> + +			<Bulk.Checkbox +				id="obfuscate" +				name="Obfuscate domains? " +				inputProps={disableInfoFields()} +			/> + +			<div className="hidden"> +				<Bulk.File +					id="json" +					fileType="application/json" +					withPreview={false} +				/> +			</div> + +			<div className="messagebutton"> +				<div> +					<button type="submit" onClick={importBlocks}>Import</button> +				</div> + +				<div> +					<button type="submit" onClick={exportBlocks}>Export</button> + +					<Bulk.Select id="exportType" name="Export type" options={ +						<> +							<option value="plain">One per line in text field</option> +							<option value="json">JSON in text field</option> +							<option value="json-download">JSON file download</option> +							<option disabled value="csv">CSV in text field (glitch-soc)</option> +							<option disabled value="csv-download">CSV file download (glitch-soc)</option> +						</> +					}/> +				</div> +				<br/> +				<div> +					{errorMsg.length > 0 &&  +						<div className="error accent">{errorMsg}</div> +					} +					{statusMsg.length > 0 && +						<div className="accent">{statusMsg}</div> +					} +				</div> +			</div> +		</div> +	); +} + +function BackButton() { +	return ( +		<Link to={base}> +			<a className="button">< back</a> +		</Link> +	); +} + +function InstancePageWrapped() { +	/* We wrap the component to generate formFields with a setter depending on the domain +		 if formFields() is used inside the same component that is re-rendered with their state, +		 inputs get re-created on every change, causing them to lose focus, and bad performance +	*/ +	let [_match, {domain}] = useRoute(`${base}/:domain`); + +	if (domain == "view") { // from form field submission +		let realDomain = (new URL(document.location)).searchParams.get("domain"); +		if (realDomain == undefined) { +			return <Redirect to={base}/>; +		} else { +			domain = realDomain; +		} +	} + +	function alterDomain([key, val]) { +		return adminActions.updateDomainBlockVal([domain, key, val]); +	} + +	const fields = formFields(alterDomain, (state) => state.admin.newInstanceBlocks[domain]); + +	return <InstancePage domain={domain} Form={fields} />; +} + +function InstancePage({domain, Form}) { +	const dispatch = Redux.useDispatch(); +	const entry = Redux.useSelector(state => state.admin.newInstanceBlocks[domain]); +	const [_location, setLocation] = useLocation(); + +	React.useEffect(() => { +		if (entry == undefined) { +			dispatch(api.admin.getEditableDomainBlock(domain)); +		} +	}, []); + +	const [errorMsg, setError] = React.useState(""); +	const [statusMsg, setStatus] = React.useState(""); + +	if (entry == undefined) { +		return "Loading..."; +	} + +	const updateBlock = submit( +		() => dispatch(api.admin.updateDomainBlock(domain)), +		{setStatus, setError} +	); + +	const removeBlock = submit( +		() => dispatch(api.admin.removeDomainBlock(domain)), +		{setStatus, setError, startStatus: "Removing", successStatus: "Removed!", onSuccess: () => { +			setLocation(base); +		}} +	); + +	return ( +		<div> +			<h1><BackButton/> Federation settings for: {domain}</h1> +			{entry.new && "No stored block yet, you can add one below:"} + +			<Form.TextArea +				id="public_comment" +				name="Public comment" +			/> + +			<Form.TextArea +				id="private_comment" +				name="Private comment" +			/> + +			<Form.Checkbox +				id="obfuscate" +				name="Obfuscate domain? " +			/> + +			<div className="messagebutton"> +				<button type="submit" onClick={updateBlock}>{entry.new ? "Add block" : "Save block"}</button> + +				{!entry.new && +					<button className="danger" onClick={removeBlock}>Remove block</button> +				} + +				{errorMsg.length > 0 &&  +					<div className="error accent">{errorMsg}</div> +				} +				{statusMsg.length > 0 && +					<div className="accent">{statusMsg}</div> +				} +			</div> +		</div> +	); +}
\ No newline at end of file diff --git a/web/source/settings-panel/admin/settings.js b/web/source/settings-panel/admin/settings.js new file mode 100644 index 000000000..845a1f924 --- /dev/null +++ b/web/source/settings-panel/admin/settings.js @@ -0,0 +1,110 @@ +/* +	GoToSocial +	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU Affero General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. + +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU Affero General Public License for more details. + +	You should have received a copy of the GNU Affero General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const Promise = require("bluebird"); +const React = require("react"); +const Redux = require("react-redux"); + +const Submit = require("../components/submit"); + +const api = require("../lib/api"); +const submit = require("../lib/submit"); + +const adminActions = require("../redux/reducers/instances").actions; + +const { +	TextInput, +	TextArea, +	File +} = require("../components/form-fields").formFields(adminActions.setAdminSettingsVal, (state) => state.instances.adminSettings); + +module.exports = function AdminSettings() { +	const dispatch = Redux.useDispatch(); + +	const [errorMsg, setError] = React.useState(""); +	const [statusMsg, setStatus] = React.useState(""); + +	const updateSettings = submit( +		() => dispatch(api.admin.updateInstance()), +		{setStatus, setError} +	); + +	return ( +		<div> +			<h1>Instance Settings</h1> +			<TextInput +				id="title" +				name="Title" +				placeHolder="My GoToSocial instance" +			/> + +			<TextArea +				id="short_description" +				name="Short description" +				placeHolder="A small testing instance for the GoToSocial alpha." +			/> +			<TextArea +				id="description" +				name="Full description" +				placeHolder="A small testing instance for the GoToSocial alpha." +			/> + +			<TextInput +				id="contact_account.username" +				name="Contact user (local account username)" +				placeHolder="admin" +			/> +			<TextInput +				id="email" +				name="Contact email" +				placeHolder="admin@example.com" +			/> + +			<TextArea +				id="terms" +				name="Terms & Conditions" +				placeHolder="" +			/> + +			{/* <div className="file-upload"> +				<h3>Instance avatar</h3> +				<div> +					<img className="preview avatar" src={instance.avatar} alt={instance.avatar ? `Avatar image for the instance` : "No instance avatar image set"} /> +					<File  +						id="avatar" +						fileType="image/*" +					/> +				</div> +			</div> + +			<div className="file-upload"> +				<h3>Instance header</h3> +				<div> +					<img className="preview header" src={instance.header} alt={instance.header ? `Header image for the instance` : "No instance header image set"} /> +					<File  +						id="header" +						fileType="image/*" +					/> +				</div> +			</div> */} +			<Submit onClick={updateSettings} label="Save" errorMsg={errorMsg} statusMsg={statusMsg} /> +		</div> +	); +};
\ No newline at end of file diff --git a/web/source/settings-panel/components/error.jsx b/web/source/settings-panel/components/error.jsx new file mode 100644 index 000000000..13dc686b7 --- /dev/null +++ b/web/source/settings-panel/components/error.jsx @@ -0,0 +1,45 @@ +/* +	GoToSocial +	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU Affero General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. + +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU Affero General Public License for more details. + +	You should have received a copy of the GNU Affero General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const Promise = require("bluebird"); +const React = require("react"); + +module.exports = function ErrorFallback({error, resetErrorBoundary}) { +	return ( +		<div className="error"> +			<p> +				{"An error occured, please report this on the "} +				<a href="https://github.com/superseriousbusiness/gotosocial/issues">GoToSocial issue tracker</a> +				{" or "} +				<a href="https://matrix.to/#/#gotosocial-help:superseriousbusiness.org">Matrix support room</a>. +				<br/>Include the details below: +			</p> +			<pre> +				{error.name}: {error.message} +			</pre> +			<pre> +				{error.stack} +			</pre> +			<p> +				<button onClick={resetErrorBoundary}>Try again</button> or <a href="">refresh the page</a> +			</p> +		</div> +	); +};
\ No newline at end of file diff --git a/web/source/settings-panel/components/fake-toot.jsx b/web/source/settings-panel/components/fake-toot.jsx new file mode 100644 index 000000000..f79e24eb9 --- /dev/null +++ b/web/source/settings-panel/components/fake-toot.jsx @@ -0,0 +1,43 @@ +/* +	GoToSocial +	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU Affero General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. + +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU Affero General Public License for more details. + +	You should have received a copy of the GNU Affero General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const React = require("react"); +const Redux = require("react-redux"); + +module.exports = function FakeToot({children}) { +	const account = Redux.useSelector((state) => state.user.profile); + +	return ( +		<div className="toot expanded"> +			<div className="contentgrid"> +				<span className="avatar"> +					<img src={account.avatar} alt=""/> +				</span> +				<span className="displayname">{account.display_name.trim().length > 0 ? account.display_name : account.username}</span> +				<span className="username">@{account.username}</span> +				<div className="text"> +					<div className="content"> +						{children} +					</div> +				</div> +			</div> +		</div> +	); +};
\ No newline at end of file diff --git a/web/source/settings-panel/components/form-fields.jsx b/web/source/settings-panel/components/form-fields.jsx new file mode 100644 index 000000000..cb402c3b2 --- /dev/null +++ b/web/source/settings-panel/components/form-fields.jsx @@ -0,0 +1,167 @@ +/* +	GoToSocial +	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU Affero General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. + +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU Affero General Public License for more details. + +	You should have received a copy of the GNU Affero General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const React = require("react"); +const Redux = require("react-redux"); +const d = require("dotty"); +const prettierBytes = require("prettier-bytes"); + +function eventListeners(dispatch, setter, obj) { +	return { +		onTextChange: function (key) { +			return function (e) { +				dispatch(setter([key, e.target.value])); +			}; +		}, +		 +		onCheckChange: function (key) { +			return function (e) { +				dispatch(setter([key, e.target.checked])); +			}; +		}, +		 +		onFileChange: function (key, withPreview) { +			return function (e) { +				let file = e.target.files[0]; +				if (withPreview) { +					let old = d.get(obj, key); +					if (old != undefined) { +						URL.revokeObjectURL(old); // no error revoking a non-Object URL as provided by instance +					} +					let objectURL = URL.createObjectURL(file); +					dispatch(setter([key, objectURL])); +				} +				dispatch(setter([`${key}File`, file])); +			}; +		} +	}; +} + +function get(state, id, defaultVal) { +	let value; +	if (id.includes(".")) { +		value = d.get(state, id); +	} else { +		value = state[id]; +	} +	if (value == undefined) { +		value = defaultVal; +	} +	return value; +} + +// function removeFile(name) { +// 	return function(e) { +// 		e.preventDefault(); +// 		dispatch(user.setProfileVal([name, ""])); +// 		dispatch(user.setProfileVal([`${name}File`, ""])); +// 	}; +// } + +module.exports = { +	formFields: function formFields(setter, selector) { +		function FormField({ +			type, id, name, className="", placeHolder="", fileType="", children=null, +			options=null, inputProps={}, withPreview=true, showSize=false, maxSize=Infinity +		}) { +			const dispatch = Redux.useDispatch(); +			let state = Redux.useSelector(selector); +			let { +				onTextChange, +				onCheckChange, +				onFileChange +			} = eventListeners(dispatch, setter, state); + +			let field; +			let defaultLabel = true; +			if (type == "text") { +				field = <input type="text" id={id} value={get(state, id, "")} placeholder={placeHolder} className={className} onChange={onTextChange(id)} {...inputProps}/>; +			} else if (type == "textarea") { +				field = <textarea type="text" id={id} value={get(state, id, "")} placeholder={placeHolder} className={className} onChange={onTextChange(id)} rows={8} {...inputProps}/>; +			} else if (type == "checkbox") { +				field = <input type="checkbox" id={id} checked={get(state, id, false)} className={className} onChange={onCheckChange(id)} {...inputProps}/>; +			} else if (type == "select") { +				field = ( +					<select id={id} value={get(state, id, "")} className={className} onChange={onTextChange(id)} {...inputProps}> +						{options} +					</select> +				); +			} else if (type == "file") { +				defaultLabel = false; +				let file = get(state, `${id}File`); + +				let size = null; +				if (showSize && file) { +					size = `(${prettierBytes(file.size)})`; + +					if (file.size > maxSize) { +						size = <span className="error-text">{size}</span>; +					} +				} + +				field = ( +					<> +						<label htmlFor={id} className="file-input button">Browse</label> +						<span> +							{file ? file.name : "no file selected"} {size} +						</span> +						{/* <a onClick={removeFile("header")}>remove</a> */} +						<input className="hidden" id={id} type="file" accept={fileType} onChange={onFileChange(id, withPreview)}  {...inputProps}/> +					</> +				); +			} else { +				defaultLabel = false; +				field = `unsupported FormField ${type}, this is a developer error`; +			} + +			let label = <label htmlFor={id}>{name}</label>; +			return ( +				<div className={`form-field ${type}`}> +					{defaultLabel ? label : null}	{field} +					{children} +				</div> +			); +		} + +		return { +			TextInput: function(props) { +				return <FormField type="text" {...props} />; +			}, +	 +			TextArea: function(props) { +				return <FormField type="textarea" {...props} />; +			}, +	 +			Checkbox: function(props) { +				return <FormField type="checkbox" {...props} />; +			}, +	 +			Select: function(props) { +				return <FormField type="select" {...props} />; +			}, +	 +			File: function(props) { +				return <FormField type="file" {...props} />; +			}, +		}; +	}, + +	eventListeners +};
\ No newline at end of file diff --git a/web/source/panels/user/languages.js b/web/source/settings-panel/components/languages.jsx index c20e08426..1522495da 100644 --- a/web/source/panels/user/languages.js +++ b/web/source/settings-panel/components/languages.jsx @@ -1,19 +1,19 @@  /* -   GoToSocial -   Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org +	GoToSocial +	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org -   This program is free software: you can redistribute it and/or modify -   it under the terms of the GNU Affero General Public License as published by -   the Free Software Foundation, either version 3 of the License, or -   (at your option) any later version. +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU Affero General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. -   This program is distributed in the hope that it will be useful, -   but WITHOUT ANY WARRANTY; without even the implied warranty of -   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -   GNU Affero General Public License for more details. +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU Affero General Public License for more details. -   You should have received a copy of the GNU Affero General Public License -   along with this program.  If not, see <http://www.gnu.org/licenses/>. +	You should have received a copy of the GNU Affero General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>.  */  "use strict"; diff --git a/web/source/settings-panel/components/login.jsx b/web/source/settings-panel/components/login.jsx new file mode 100644 index 000000000..c67e99acd --- /dev/null +++ b/web/source/settings-panel/components/login.jsx @@ -0,0 +1,102 @@ +/* +	GoToSocial +	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU Affero General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. + +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU Affero General Public License for more details. + +  You should have received a copy of the GNU Affero General Public License +  along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ +	 +"use strict"; + +const Promise = require("bluebird"); +const React = require("react"); +const Redux = require("react-redux"); + +const { setInstance } = require("../redux/reducers/oauth").actions; +const api = require("../lib/api"); + +module.exports = function Login({error}) { +	const dispatch = Redux.useDispatch(); +	const [ instanceField, setInstanceField ] = React.useState(""); +	const [ errorMsg, setErrorMsg ] = React.useState(); +	const instanceFieldRef = React.useRef(""); + +	React.useEffect(() => { +		// check if current domain runs an instance +		let currentDomain = window.location.origin; +		Promise.try(() => { +			return dispatch(api.instance.fetchWithoutStore(currentDomain)); +		}).then(() => { +			if (instanceFieldRef.current.length == 0) { // user hasn't started typing yet +				dispatch(setInstance(currentDomain)); +				instanceFieldRef.current = currentDomain; +				setInstanceField(currentDomain); +			} +		}).catch((e) => { +			console.log("Current domain does not host a valid instance: ", e); +		}); +	}, []); + +	function tryInstance() { +		let domain = instanceFieldRef.current; +		Promise.try(() => { +			return dispatch(api.instance.fetchWithoutStore(domain)).catch((e) => { +				// TODO: clearer error messages for common errors +				console.log(e); +				throw e; +			}); +		}).then(() => { +			dispatch(setInstance(domain)); + +			return dispatch(api.oauth.register()).catch((e) => { +				console.log(e); +				throw e; +			}); +		}).then(() => { +			return dispatch(api.oauth.authorize()); // will send user off-page +		}).catch((e) => { +			setErrorMsg( +				<> +					<b>{e.type}</b> +					<span>{e.message}</span> +				</> +			); +		}); +	} + +	function updateInstanceField(e) { +		if (e.key == "Enter") { +			tryInstance(instanceField); +		} else { +			setInstanceField(e.target.value); +			instanceFieldRef.current = e.target.value; +		} +	} + +	return ( +		<section className="login"> +			<h1>OAUTH Login:</h1> +			{error} +			<form onSubmit={(e) => e.preventDefault()}> +				<label htmlFor="instance">Instance: </label> +				<input value={instanceField} onChange={updateInstanceField} id="instance"/> +				{errorMsg &&  +				<div className="error"> +					{errorMsg} +				</div> +				} +				<button onClick={tryInstance}>Authenticate</button> +			</form> +		</section> +	); +};
\ No newline at end of file diff --git a/web/source/settings-panel/components/nav-button.jsx b/web/source/settings-panel/components/nav-button.jsx new file mode 100644 index 000000000..3c76711fb --- /dev/null +++ b/web/source/settings-panel/components/nav-button.jsx @@ -0,0 +1,33 @@ +/* +	GoToSocial +	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU Affero General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. + +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU Affero General Public License for more details. + +	You should have received a copy of the GNU Affero General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const React = require("react"); +const { Link, useRoute } = require("wouter"); + +module.exports = function NavButton({href, name}) { +	const [isActive] = useRoute(`${href}/:anything?`); +	return ( +		<Link href={href}> +			<a className={isActive ? "active" : ""} data-content={name}> +				{name} +			</a> +		</Link> +	); +};
\ No newline at end of file diff --git a/web/source/settings-panel/components/submit.jsx b/web/source/settings-panel/components/submit.jsx new file mode 100644 index 000000000..0187fc81f --- /dev/null +++ b/web/source/settings-panel/components/submit.jsx @@ -0,0 +1,35 @@ +/* +	GoToSocial +	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU Affero General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. + +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU Affero General Public License for more details. + +	You should have received a copy of the GNU Affero General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const React = require("react"); + +module.exports = function Submit({onClick, label, errorMsg, statusMsg}) { +	return ( +		<div className="messagebutton"> +			<button type="submit" onClick={onClick}>{ label }</button> +			{errorMsg.length > 0 &&  +				<div className="error accent">{errorMsg}</div> +			} +			{statusMsg.length > 0 && +				<div className="accent">{statusMsg}</div> +			} +		</div> +	); +}; diff --git a/web/source/settings-panel/index.js b/web/source/settings-panel/index.js new file mode 100644 index 000000000..34720e818 --- /dev/null +++ b/web/source/settings-panel/index.js @@ -0,0 +1,178 @@ +/* +	GoToSocial +	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU Affero General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. + +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU Affero General Public License for more details. + +	You should have received a copy of the GNU Affero General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const Promise = require("bluebird"); +const React = require("react"); +const ReactDom = require("react-dom/client"); +const Redux = require("react-redux"); +const { Switch, Route, Redirect } = require("wouter"); +const { Provider } = require("react-redux"); +const { PersistGate } = require("redux-persist/integration/react"); + +const { store, persistor } = require("./redux"); +const api = require("./lib/api"); +const oauth = require("./redux/reducers/oauth").actions; +const { AuthenticationError } = require("./lib/errors"); + +const Login = require("./components/login"); + +require("./style.css"); + +// TODO: nested categories? +const nav = { +	"User": { +		"Profile": require("./user/profile.js"), +		"Settings": require("./user/settings.js"), +	}, +	"Admin": { +		adminOnly: true, +		"Instance Settings": require("./admin/settings.js"), +		"Actions": require("./admin/actions"), +		"Federation": require("./admin/federation.js"), +		"Custom Emoji": require("./admin/emoji.js"), +	} +}; + +const { sidebar, panelRouter } = require("./lib/get-views")(nav); + +function App() { +	const dispatch = Redux.useDispatch(); + +	const { loginState, isAdmin } = Redux.useSelector((state) => state.oauth); +	const reduxTempStatus = Redux.useSelector((state) => state.temporary.status); + +	const [errorMsg, setErrorMsg] = React.useState(); +	const [tokenChecked, setTokenChecked] = React.useState(false); + +	React.useEffect(() => { +		if (loginState == "login" || loginState == "callback") { +			Promise.try(() => { +				// Process OAUTH authorization token from URL if available +				if (loginState == "callback") { +					let urlParams = new URLSearchParams(window.location.search); +					let code = urlParams.get("code"); + +					if (code == undefined) { +						setErrorMsg(new Error("Waiting for OAUTH callback but no ?code= provided. You can try logging in again:")); +					} else { +						return dispatch(api.oauth.tokenize(code)); +					} +				} +			}).then(() => { +				// Fetch current instance info +				return dispatch(api.instance.fetch()); +			}).then(() => { +				// Check currently stored auth token for validity if available +				return dispatch(api.user.fetchAccount()); +			}).then(() => { +				setTokenChecked(true); + +				return dispatch(api.oauth.checkIfAdmin()); +			}).catch((e) => { +				if (e instanceof AuthenticationError) { +					dispatch(oauth.remove()); +					e.message = "Stored OAUTH token no longer valid, please log in again."; +				} +				setErrorMsg(e); +				console.error(e); +			}); +		} +	}, []); + +	let ErrorElement = null; +	if (errorMsg != undefined) { +		ErrorElement = ( +			<div className="error"> +				<b>{errorMsg.type}</b> +				<span>{errorMsg.message}</span> +			</div> +		); +	} + +	const LogoutElement = ( +		<button className="logout" onClick={() => { dispatch(api.oauth.logout()); }}> +			Log out +		</button> +	); + +	if (reduxTempStatus != undefined) { +		return ( +			<section> +				{reduxTempStatus} +			</section> +		); +	} else if (tokenChecked && loginState == "login") { +		return ( +			<> +				<div className="sidebar"> +					{sidebar.all} +					{isAdmin && sidebar.admin} +					{LogoutElement} +				</div> +				<section className="with-sidebar"> +					{ErrorElement} +					<Switch> +						{panelRouter.all} +						{isAdmin && panelRouter.admin} +						<Route> {/* default route */} +							<Redirect to="/settings/user" /> +						</Route> +					</Switch> +				</section> +			</> +		); +	} else if (loginState == "none") { +		return ( +			<Login error={ErrorElement} /> +		); +	} else { +		let status; + +		if (loginState == "login") { +			status = "Verifying stored login..."; +		} else if (loginState == "callback") { +			status = "Processing OAUTH callback..."; +		} + +		return ( +			<section> +				<div> +					{status} +				</div> +				{ErrorElement} +				{LogoutElement} +			</section> +		); +	} + +} + +function Main() { +	return ( +		<Provider store={store}> +			<PersistGate loading={"loading..."} persistor={persistor}> +				<App /> +			</PersistGate> +		</Provider> +	); +} + +const root = ReactDom.createRoot(document.getElementById("root")); +root.render(<React.StrictMode><Main /></React.StrictMode>);
\ No newline at end of file diff --git a/web/source/settings-panel/lib/api/admin.js b/web/source/settings-panel/lib/api/admin.js new file mode 100644 index 000000000..1df47b693 --- /dev/null +++ b/web/source/settings-panel/lib/api/admin.js @@ -0,0 +1,192 @@ +/* +	GoToSocial +	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU Affero General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. + +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU Affero General Public License for more details. + +	You should have received a copy of the GNU Affero General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const Promise = require("bluebird"); +const isValidDomain = require("is-valid-domain"); + +const instance = require("../../redux/reducers/instances").actions; +const admin = require("../../redux/reducers/admin").actions; + +module.exports = function ({ apiCall, getChanges }) { +	const adminAPI = { +		updateInstance: function updateInstance() { +			return function (dispatch, getState) { +				return Promise.try(() => { +					const state = getState().instances.adminSettings; + +					const update = getChanges(state, { +						formKeys: ["title", "short_description", "description", "contact_account.username", "email", "terms"], +						renamedKeys: {"contact_account.username": "contact_username"}, +						// fileKeys: ["avatar", "header"] +					}); + +					return dispatch(apiCall("PATCH", "/api/v1/instance", update, "form")); +				}).then((data) => { +					return dispatch(instance.setInstanceInfo(data)); +				}); +			}; +		}, + +		fetchDomainBlocks: function fetchDomainBlocks() { +			return function (dispatch, _getState) { +				return Promise.try(() => { +					return dispatch(apiCall("GET", "/api/v1/admin/domain_blocks")); +				}).then((data) => { +					return dispatch(admin.setBlockedInstances(data)); +				}); +			}; +		}, + +		updateDomainBlock: function updateDomainBlock(domain) { +			return function (dispatch, getState) { +				return Promise.try(() => { +					const state = getState().admin.newInstanceBlocks[domain]; +					const update = getChanges(state, { +						formKeys: ["domain", "obfuscate", "public_comment", "private_comment"], +					}); + +					return dispatch(apiCall("POST", "/api/v1/admin/domain_blocks", update, "form")); +				}).then((block) => { +					return Promise.all([ +						dispatch(admin.newDomainBlock([domain, block])), +						dispatch(admin.setDomainBlock([domain, block])) +					]); +				}); +			}; +		}, + +		getEditableDomainBlock: function getEditableDomainBlock(domain) { +			return function (dispatch, getState) { +				let data = getState().admin.blockedInstances[domain]; +				return dispatch(admin.newDomainBlock([domain, data])); +			}; +		}, + +		bulkDomainBlock: function bulkDomainBlock() { +			return function (dispatch, getState) { +				let invalidDomains = []; +				let success = 0; + +				return Promise.try(() => { +					const state = getState().admin.bulkBlock; +					let list = state.list; +					let domains; + +					let fields = getChanges(state, { +						formKeys: ["obfuscate", "public_comment", "private_comment"] +					}); + +					let defaultDate = new Date().toUTCString(); +					 +					if (list[0] == "[") { +						domains = JSON.parse(state.list); +					} else { +						domains = list.split("\n").map((line_) => { +							let line = line_.trim(); +							if (line.length == 0) { +								return null; +							} + +							if (!isValidDomain(line, {wildcard: true, allowUnicode: true})) { +								invalidDomains.push(line); +								return null; +							} + +							return { +								domain: line, +								created_at: defaultDate, +								...fields +							}; +						}).filter((a) => a != null); +					} + +					if (domains.length == 0) { +						return; +					} + +					const update = { +						domains: new Blob([JSON.stringify(domains)], {type: "application/json"}) +					}; + +					return dispatch(apiCall("POST", "/api/v1/admin/domain_blocks?import=true", update, "form")); +				}).then((blocks) => { +					if (blocks != undefined) { +						return Promise.each(blocks, (block) => { +							success += 1; +							return dispatch(admin.setDomainBlock([block.domain, block])); +						}); +					} +				}).then(() => { +					return { +						success, +						invalidDomains +					}; +				}); +			}; +		}, + +		removeDomainBlock: function removeDomainBlock(domain) { +			return function (dispatch, getState) { +				return Promise.try(() => { +					const id = getState().admin.blockedInstances[domain].id; +					return dispatch(apiCall("DELETE", `/api/v1/admin/domain_blocks/${id}`)); +				}).then((removed) => { +					return dispatch(admin.removeDomainBlock(removed.domain)); +				}); +			}; +		}, + +		mediaCleanup: function mediaCleanup(days) { +			return function (dispatch, _getState) { +				return Promise.try(() => { +					return dispatch(apiCall("POST", `/api/v1/admin/media_cleanup?remote_cache_days=${days}`)); +				}); +			}; +		}, + +		fetchCustomEmoji: function fetchCustomEmoji() { +			return function (dispatch, _getState) { +				return Promise.try(() => { +					return dispatch(apiCall("GET", "/api/v1/custom_emojis")); +				}).then((emoji) => { +					return dispatch(admin.setEmoji(emoji)); +				}); +			}; +		}, + +		newEmoji: function newEmoji() { +			return function (dispatch, getState) { +				return Promise.try(() => { +					const state = getState().admin.newEmoji; + +					const update = getChanges(state, { +						formKeys: ["shortcode"], +						fileKeys: ["image"] +					}); + +					return dispatch(apiCall("POST", "/api/v1/admin/custom_emojis", update, "form")); +				}).then((emoji) => { +					return dispatch(admin.addEmoji(emoji)); +				}); +			}; +		} +	}; +	return adminAPI; +};
\ No newline at end of file diff --git a/web/source/settings-panel/lib/api/index.js b/web/source/settings-panel/lib/api/index.js new file mode 100644 index 000000000..e699011bd --- /dev/null +++ b/web/source/settings-panel/lib/api/index.js @@ -0,0 +1,185 @@ +/* +	GoToSocial +	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU Affero General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. + +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU Affero General Public License for more details. + +	You should have received a copy of the GNU Affero General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const Promise = require("bluebird"); +const { isPlainObject } = require("is-plain-object"); +const d = require("dotty"); + +const { APIError, AuthenticationError } = require("../errors"); +const { setInstanceInfo, setNamedInstanceInfo } = require("../../redux/reducers/instances").actions; +const oauth = require("../../redux/reducers/oauth").actions; + +function apiCall(method, route, payload, type = "json") { +	return function (dispatch, getState) { +		const state = getState(); +		let base = state.oauth.instance; +		let auth = state.oauth.token; +		console.log(method, base, route, "auth:", auth != undefined); + +		return Promise.try(() => { +			let url = new URL(base); +			let [path, query] = route.split("?"); +			url.pathname = path; +			if (query != undefined) { +				url.search = query; +			} +			let body; + +			let headers = { +				"Accept": "application/json", +			}; + +			if (payload != undefined) { +				if (type == "json") { +					headers["Content-Type"] = "application/json"; +					body = JSON.stringify(payload); +				} else if (type == "form") { +					const formData = new FormData(); +					Object.entries(payload).forEach(([key, val]) => { +						if (isPlainObject(val)) { +							Object.entries(val).forEach(([key2, val2]) => { +								if (val2 != undefined) { +									formData.set(`${key}[${key2}]`, val2); +								} +							}); +						} else { +							if (val != undefined) { +								formData.set(key, val); +							} +						} +					}); +					body = formData; +				} +			} + +			if (auth != undefined) { +				headers["Authorization"] = auth; +			} + +			return fetch(url.toString(), { +				method, +				headers, +				body +			}); +		}).then((res) => { +			// try parse json even with error +			let json = res.json().catch((e) => { +				throw new APIError(`JSON parsing error: ${e.message}`); +			}); + +			return Promise.all([res, json]); +		}).then(([res, json]) => { +			if (!res.ok) { +				if (auth != undefined && (res.status == 401 || res.status == 403)) { +					// stored access token is invalid +					throw new AuthenticationError("401: Authentication error", {json, status: res.status}); +				} else { +					throw new APIError(json.error, { json }); +				} +			} else { +				return json; +			} +		}); +	}; +} + +function getChanges(state, keys) { +	const { formKeys = [], fileKeys = [], renamedKeys = {} } = keys; +	const update = {}; + +	formKeys.forEach((key) => { +		let value = d.get(state, key); +		if (value == undefined) { +			return; +		} +		if (renamedKeys[key]) { +			key = renamedKeys[key]; +		} +		d.put(update, key, value); +	}); + +	fileKeys.forEach((key) => { +		let file = d.get(state, `${key}File`); +		if (file != undefined) { +			if (renamedKeys[key]) { +				key = renamedKeys[key]; +			} +			d.put(update, key, file); +		} +	}); + +	return update; +} + +function getCurrentUrl() { +	return `${window.location.origin}${window.location.pathname}`; +} + +function fetchInstanceWithoutStore(domain) { +	return function (dispatch, getState) { +		return Promise.try(() => { +			let lookup = getState().instances.info[domain]; +			if (lookup != undefined) { +				return lookup; +			} + +			// apiCall expects to pull the domain from state, +			// but we don't want to store it there yet +			// so we mock the API here with our function argument +			let fakeState = { +				oauth: { instance: domain } +			}; + +			return apiCall("GET", "/api/v1/instance")(dispatch, () => fakeState); +		}).then((json) => { +			if (json && json.uri) { // TODO: validate instance json more? +				dispatch(setNamedInstanceInfo([domain, json])); +				return json; +			} +		}); +	}; +} + +function fetchInstance() { +	return function (dispatch, _getState) { +		return Promise.try(() => { +			return dispatch(apiCall("GET", "/api/v1/instance")); +		}).then((json) => { +			if (json && json.uri) { +				dispatch(setInstanceInfo(json)); +				return json; +			} +		}); +	}; +} + +let submoduleArgs = { apiCall, getCurrentUrl, getChanges }; + +module.exports = { +	instance: { +		fetchWithoutStore: fetchInstanceWithoutStore, +		fetch: fetchInstance +	}, +	oauth: require("./oauth")(submoduleArgs), +	user: require("./user")(submoduleArgs), +	admin: require("./admin")(submoduleArgs), +	apiCall, +	getChanges +};
\ No newline at end of file diff --git a/web/source/settings-panel/lib/api/oauth.js b/web/source/settings-panel/lib/api/oauth.js new file mode 100644 index 000000000..76d0e9d2f --- /dev/null +++ b/web/source/settings-panel/lib/api/oauth.js @@ -0,0 +1,124 @@ +/* +	GoToSocial +	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU Affero General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. + +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU Affero General Public License for more details. + +	You should have received a copy of the GNU Affero General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const Promise = require("bluebird"); + +const { OAUTHError, AuthenticationError } = require("../errors"); + +const oauth = require("../../redux/reducers/oauth").actions; +const temporary = require("../../redux/reducers/temporary").actions; +const admin = require("../../redux/reducers/admin").actions; + +module.exports = function oauthAPI({ apiCall, getCurrentUrl }) { +	return { + +		register: function register(scopes = []) { +			return function (dispatch, _getState) { +				return Promise.try(() => { +					return dispatch(apiCall("POST", "/api/v1/apps", { +						client_name: "GoToSocial Settings", +						scopes: scopes.join(" "), +						redirect_uris: getCurrentUrl(), +						website: getCurrentUrl() +					})); +				}).then((json) => { +					json.scopes = scopes; +					dispatch(oauth.setRegistration(json)); +				}); +			}; +		}, + +		authorize: function authorize() { +			return function (dispatch, getState) { +				let state = getState(); +				let reg = state.oauth.registration; +				let base = new URL(state.oauth.instance); + +				base.pathname = "/oauth/authorize"; +				base.searchParams.set("client_id", reg.client_id); +				base.searchParams.set("redirect_uri", getCurrentUrl()); +				base.searchParams.set("response_type", "code"); +				base.searchParams.set("scope", reg.scopes.join(" ")); + +				dispatch(oauth.setLoginState("callback")); +				dispatch(temporary.setStatus("Redirecting to instance login...")); + +				// send user to instance's login flow +				window.location.assign(base.href); +			}; +		}, + +		tokenize: function tokenize(code) { +			return function (dispatch, getState) { +				let reg = getState().oauth.registration; + +				return Promise.try(() => { +					if (reg == undefined || reg.client_id == undefined) { +						throw new OAUTHError("Callback code present, but no client registration is available from localStorage. \nNote: localStorage is unavailable in Private Browsing."); +					} + +					return dispatch(apiCall("POST", "/oauth/token", { +						client_id: reg.client_id, +						client_secret: reg.client_secret, +						redirect_uri: getCurrentUrl(), +						grant_type: "authorization_code", +						code: code +					})); +				}).then((json) => { +					window.history.replaceState({}, document.title, window.location.pathname); +					return dispatch(oauth.login(json)); +				}); +			}; +		}, + +		checkIfAdmin: function checkIfAdmin() { +			return function (dispatch, getState) { +				const state = getState(); +				let stored = state.oauth.isAdmin; +				if (stored != undefined) { +					return stored; +				} + +				// newer GoToSocial version will include a `role` in the Account data, check that first +				// TODO: check account data for admin status				 + +				// no role info, try fetching an admin-only route and see if we get an error +				return Promise.try(() => { +					return dispatch(apiCall("GET", "/api/v1/admin/domain_blocks")); +				}).then((data) => { +					return Promise.all([ +						dispatch(oauth.setAdmin(true)), +						dispatch(admin.setBlockedInstances(data)) +					]); +				}).catch(AuthenticationError, () => { +					return dispatch(oauth.setAdmin(false)); +				}); +			}; +		}, + +		logout: function logout() { +			return function (dispatch, _getState) { +				// TODO: GoToSocial does not have a logout API route yet + +				return dispatch(oauth.remove()); +			}; +		} +	}; +};
\ No newline at end of file diff --git a/web/source/settings-panel/lib/api/user.js b/web/source/settings-panel/lib/api/user.js new file mode 100644 index 000000000..18b54bd73 --- /dev/null +++ b/web/source/settings-panel/lib/api/user.js @@ -0,0 +1,67 @@ +/* +	GoToSocial +	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU Affero General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. + +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU Affero General Public License for more details. + +	You should have received a copy of the GNU Affero General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const Promise = require("bluebird"); + +const user = require("../../redux/reducers/user").actions; + +module.exports = function ({ apiCall, getChanges }) { +	function updateCredentials(selector, keys) { +		return function (dispatch, getState) { +			return Promise.try(() => { +				const state = selector(getState()); + +				const update = getChanges(state, keys); + +				return dispatch(apiCall("PATCH", "/api/v1/accounts/update_credentials", update, "form")); +			}).then((account) => { +				return dispatch(user.setAccount(account)); +			}); +		}; +	} + +	return { +		fetchAccount: function fetchAccount() { +			return function (dispatch, _getState) { +				return Promise.try(() => { +					return dispatch(apiCall("GET", "/api/v1/accounts/verify_credentials")); +				}).then((account) => { +					return dispatch(user.setAccount(account)); +				}); +			}; +		}, + +		updateProfile: function updateProfile() { +			const formKeys = ["display_name", "locked", "source", "custom_css", "source.note"]; +			const renamedKeys = { +				"source.note": "note" +			}; +			const fileKeys = ["header", "avatar"]; + +			return updateCredentials((state) => state.user.profile, {formKeys, renamedKeys, fileKeys}); +		}, + +		updateSettings: function updateProfile() { +			const formKeys = ["source"]; + +			return updateCredentials((state) => state.user.settings, {formKeys}); +		} +	}; +};
\ No newline at end of file diff --git a/web/source/settings-panel/lib/errors.js b/web/source/settings-panel/lib/errors.js new file mode 100644 index 000000000..c2f781cb2 --- /dev/null +++ b/web/source/settings-panel/lib/errors.js @@ -0,0 +1,27 @@ +/* +	GoToSocial +	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU Affero General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. + +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU Affero General Public License for more details. + +  You should have received a copy of the GNU Affero General Public License +  along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ +	 +"use strict"; + +const createError = require("create-error"); + +module.exports = { +	APIError: createError("APIError"), +	OAUTHError: createError("OAUTHError"), +	AuthenticationError: createError("AuthenticationError"), +};
\ No newline at end of file diff --git a/web/source/settings-panel/lib/get-views.js b/web/source/settings-panel/lib/get-views.js new file mode 100644 index 000000000..39f627435 --- /dev/null +++ b/web/source/settings-panel/lib/get-views.js @@ -0,0 +1,102 @@ +/* +	GoToSocial +	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU Affero General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. + +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU Affero General Public License for more details. + +	You should have received a copy of the GNU Affero General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const React = require("react"); +const Redux = require("react-redux"); +const { Link, Route, Switch, Redirect } = require("wouter"); +const { ErrorBoundary } = require("react-error-boundary"); + +const ErrorFallback = require("../components/error"); +const NavButton = require("../components/nav-button"); + +function urlSafe(str) { +	return str.toLowerCase().replace(/\s+/g, "-"); +} + +module.exports = function getViews(struct) { +	const sidebar = { +		all: [], +		admin: [], +	}; + +	const panelRouter = { +		all: [], +		admin: [], +	}; + +	Object.entries(struct).forEach(([name, entries]) => { +		let sidebarEl = sidebar.all; +		let panelRouterEl = panelRouter.all; + +		if (entries.adminOnly) { +			sidebarEl = sidebar.admin; +			panelRouterEl = panelRouter.admin; +			delete entries.adminOnly; +		} + +		let base = `/settings/${urlSafe(name)}`; + +		let links = []; + +		let firstRoute; + +		Object.entries(entries).forEach(([name, ViewComponent]) => { +			let url = `${base}/${urlSafe(name)}`; + +			if (firstRoute == undefined) { +				firstRoute = url; +			} + +			panelRouterEl.push(( +				<Route path={`${url}/:page?`} key={url}> +					<ErrorBoundary FallbackComponent={ErrorFallback} onReset={() => { }}> +						{/* FIXME: implement onReset */} +						<ViewComponent /> +					</ErrorBoundary> +				</Route> +			)); + +			links.push( +				<NavButton key={url} href={url} name={name} /> +			); +		}); + +		panelRouterEl.push( +			<Route key={base} path={base}> +				<Redirect to={firstRoute} /> +			</Route> +		); + +		sidebarEl.push( +			<React.Fragment key={name}> +				<Link href={firstRoute}> +					<a> +						<h2>{name}</h2> +					</a> +				</Link> +				<nav> +					{links} +				</nav> +			</React.Fragment> +		); +	}); + +	return { sidebar, panelRouter }; +};
\ No newline at end of file diff --git a/web/source/panels/lib/panel.js b/web/source/settings-panel/lib/panel.js index 168eac7a0..df723bc74 100644 --- a/web/source/panels/lib/panel.js +++ b/web/source/settings-panel/lib/panel.js @@ -1,19 +1,19 @@  /* -   GoToSocial -   Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org +	GoToSocial +	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org -   This program is free software: you can redistribute it and/or modify -   it under the terms of the GNU Affero General Public License as published by -   the Free Software Foundation, either version 3 of the License, or -   (at your option) any later version. +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU Affero General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. -   This program is distributed in the hope that it will be useful, -   but WITHOUT ANY WARRANTY; without even the implied warranty of -   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -   GNU Affero General Public License for more details. +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU Affero General Public License for more details. -   You should have received a copy of the GNU Affero General Public License -   along with this program.  If not, see <http://www.gnu.org/licenses/>. +	You should have received a copy of the GNU Affero General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>.  */  "use strict"; diff --git a/web/source/settings-panel/lib/submit.js b/web/source/settings-panel/lib/submit.js new file mode 100644 index 000000000..f268b5cf9 --- /dev/null +++ b/web/source/settings-panel/lib/submit.js @@ -0,0 +1,48 @@ +/* +	GoToSocial +	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU Affero General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. + +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU Affero General Public License for more details. + +	You should have received a copy of the GNU Affero General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const Promise = require("bluebird"); + +module.exports = function submit(func, { +	setStatus, setError, +	startStatus="PATCHing", successStatus="Saved!", +	onSuccess, +	onError +}) { +	return function() { +		setStatus(startStatus); +		setError(""); +		return Promise.try(() => { +			return func(); +		}).then(() => { +			setStatus(successStatus); +			if (onSuccess != undefined) { +				return onSuccess(); +			} +		}).catch((e) => { +			setError(e.message); +			setStatus(""); +			console.error(e); +			if (onError != undefined) { +				onError(e); +			} +		}); +	}; +};
\ No newline at end of file diff --git a/web/source/settings-panel/redux/index.js b/web/source/settings-panel/redux/index.js new file mode 100644 index 000000000..e0dbe9b23 --- /dev/null +++ b/web/source/settings-panel/redux/index.js @@ -0,0 +1,48 @@ +/* +	 GoToSocial +	 Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + +	 This program is free software: you can redistribute it and/or modify +	 it under the terms of the GNU Affero General Public License as published by +	 the Free Software Foundation, either version 3 of the License, or +	 (at your option) any later version. + +	 This program is distributed in the hope that it will be useful, +	 but WITHOUT ANY WARRANTY; without even the implied warranty of +	 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	 GNU Affero General Public License for more details. + +	 You should have received a copy of the GNU Affero General Public License +	 along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const { createStore, combineReducers, applyMiddleware } = require("redux"); +const { persistStore, persistReducer } = require("redux-persist"); +const thunk = require("redux-thunk").default; +const { composeWithDevTools } = require("redux-devtools-extension"); + +const persistConfig = { +	key: "gotosocial-settings", +	storage: require("redux-persist/lib/storage").default, +	stateReconciler: require("redux-persist/lib/stateReconciler/autoMergeLevel2").default, +	whitelist: ["oauth"], +	blacklist: ["temporary"] +}; + +const combinedReducers = combineReducers({ +	oauth: require("./reducers/oauth").reducer, +	instances: require("./reducers/instances").reducer, +	temporary: require("./reducers/temporary").reducer, +	user: require("./reducers/user").reducer, +	admin: require("./reducers/admin").reducer, +}); + +const persistedReducer = persistReducer(persistConfig, combinedReducers); +const composedEnhancer = composeWithDevTools(applyMiddleware(thunk)); + +const store = createStore(persistedReducer, composedEnhancer); +const persistor = persistStore(store); + +module.exports = { store, persistor };
\ No newline at end of file diff --git a/web/source/settings-panel/redux/reducers/admin.js b/web/source/settings-panel/redux/reducers/admin.js new file mode 100644 index 000000000..20d3d748d --- /dev/null +++ b/web/source/settings-panel/redux/reducers/admin.js @@ -0,0 +1,131 @@ +/* +	GoToSocial +	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU Affero General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. + +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU Affero General Public License for more details. + +	You should have received a copy of the GNU Affero General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const { createSlice } = require("@reduxjs/toolkit"); +const defaultValue = require("default-value"); + +function sortBlocks(blocks) { +	return blocks.sort((a, b) => { // alphabetical sort +		return a.domain.localeCompare(b.domain); +	}); +} + +function emptyBlock() { +	return { +		public_comment: "", +		private_comment: "", +		obfuscate: false +	}; +} + +function emptyEmojiForm() { +	return { +		shortcode: "" +	}; +} + +module.exports = createSlice({ +	name: "admin", +	initialState: { +		loadedBlockedInstances: false, +		blockedInstances: undefined, +		bulkBlock: { +			list: "", +			exportType: "plain", +			...emptyBlock() +		}, +		newInstanceBlocks: {}, +		emoji: {}, +		newEmoji: emptyEmojiForm() +	}, +	reducers: { +		setBlockedInstances: (state, { payload }) => { +			state.blockedInstances = {}; +			sortBlocks(payload).forEach((entry) => { +				state.blockedInstances[entry.domain] = entry; +			}); +			state.loadedBlockedInstances = true; +		}, + +		newDomainBlock: (state, { payload: [domain, data] }) => { +			if (data == undefined) { +				data = { +					new: true, +					domain, +					...emptyBlock() +				}; +			} +			state.newInstanceBlocks[domain] = data; +		}, + +		setDomainBlock: (state, { payload: [domain, data = {}] }) => { +			state.blockedInstances[domain] = data; +		}, + +		removeDomainBlock: (state, {payload: domain}) => { +			delete state.blockedInstances[domain]; +		}, + +		updateDomainBlockVal: (state, { payload: [domain, key, val] }) => { +			state.newInstanceBlocks[domain][key] = val; +		}, + +		updateBulkBlockVal: (state, { payload: [key, val] }) => { +			state.bulkBlock[key] = val; +		}, + +		resetBulkBlockVal: (state, { _payload }) => { +			state.bulkBlock = { +				list: "", +				exportType: "plain", +				...emptyBlock() +			}; +		}, + +		exportToField: (state, { _payload }) => { +			state.bulkBlock.list = Object.values(state.blockedInstances).map((entry) => { +				return entry.domain; +			}).join("\n"); +		}, + +		setEmoji: (state, {payload}) => { +			state.emoji = {}; +			payload.forEach((emoji) => { +				if (emoji.category == undefined) { +					emoji.category = "Unsorted"; +				} +				state.emoji[emoji.category] = defaultValue(state.emoji[emoji.category], []); +				state.emoji[emoji.category].push(emoji); +			}); +		}, + +		updateNewEmojiVal: (state, { payload: [key, val] }) => { +			state.newEmoji[key] = val; +		}, + +		addEmoji: (state, {payload: emoji}) => { +			if (emoji.category == undefined) { +				emoji.category = "Unsorted"; +			} +			state.emoji[emoji.category] = defaultValue(state.emoji[emoji.category], []); +			state.emoji[emoji.category].push(emoji); +		}, +	} +});
\ No newline at end of file diff --git a/web/source/settings-panel/redux/reducers/instances.js b/web/source/settings-panel/redux/reducers/instances.js new file mode 100644 index 000000000..3ad5bb7cb --- /dev/null +++ b/web/source/settings-panel/redux/reducers/instances.js @@ -0,0 +1,42 @@ +/* +	GoToSocial +	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU Affero General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. + +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU Affero General Public License for more details. + +	You should have received a copy of the GNU Affero General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const {createSlice} = require("@reduxjs/toolkit"); +const d = require("dotty"); + +module.exports = createSlice({ +	name: "instances", +	initialState: { +		info: {}, +	}, +	reducers: { +		setNamedInstanceInfo: (state, {payload}) => { +			let [key, info] = payload; +			state.info[key] = info; +		}, +		setInstanceInfo: (state, {payload}) => { +			state.current = payload; +			state.adminSettings = payload; +		}, +		setAdminSettingsVal: (state, {payload: [key, val]}) => { +			d.put(state.adminSettings, key, val); +		} +	} +});
\ No newline at end of file diff --git a/web/source/settings-panel/redux/reducers/oauth.js b/web/source/settings-panel/redux/reducers/oauth.js new file mode 100644 index 000000000..c332a7d06 --- /dev/null +++ b/web/source/settings-panel/redux/reducers/oauth.js @@ -0,0 +1,52 @@ +/* +	GoToSocial +	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU Affero General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. + +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU Affero General Public License for more details. + +	You should have received a copy of the GNU Affero General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const {createSlice} = require("@reduxjs/toolkit"); + +module.exports = createSlice({ +	name: "oauth", +	initialState: { +		loginState: 'none', +	}, +	reducers: { +		setInstance: (state, {payload}) => { +			state.instance = payload; +		}, +		setRegistration: (state, {payload}) => { +			state.registration = payload; +		}, +		setLoginState: (state, {payload}) => { +			state.loginState = payload; +		}, +		login: (state, {payload}) => { +			state.token = `${payload.token_type} ${payload.access_token}`; +			state.loginState = "login"; +		}, +		remove: (state, {_payload}) => { +			delete state.token; +			delete state.registration; +			delete state.isAdmin; +			state.loginState = "none"; +		}, +		setAdmin: (state, {payload}) => { +			state.isAdmin = payload; +		} +	} +});
\ No newline at end of file diff --git a/web/source/settings-panel/redux/reducers/temporary.js b/web/source/settings-panel/redux/reducers/temporary.js new file mode 100644 index 000000000..c887d2eee --- /dev/null +++ b/web/source/settings-panel/redux/reducers/temporary.js @@ -0,0 +1,32 @@ +/* +	GoToSocial +	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU Affero General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. + +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU Affero General Public License for more details. + +	You should have received a copy of the GNU Affero General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const {createSlice} = require("@reduxjs/toolkit"); + +module.exports = createSlice({ +	name: "temporary", +	initialState: { +	}, +	reducers: { +		setStatus: function(state, {payload}) { +			state.status = payload; +		} +	} +});
\ No newline at end of file diff --git a/web/source/settings-panel/redux/reducers/user.js b/web/source/settings-panel/redux/reducers/user.js new file mode 100644 index 000000000..b4463c9f9 --- /dev/null +++ b/web/source/settings-panel/redux/reducers/user.js @@ -0,0 +1,51 @@ +/* +	GoToSocial +	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU Affero General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. + +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU Affero General Public License for more details. + +	You should have received a copy of the GNU Affero General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const { createSlice } = require("@reduxjs/toolkit"); +const d = require("dotty"); +const defaultValue = require("default-value"); + +module.exports = createSlice({ +	name: "user", +	initialState: { +		profile: {}, +		settings: {} +	}, +	reducers: { +		setAccount: (state, { payload }) => { +			payload.source = defaultValue(payload.source, {}); +			payload.source.language = defaultValue(payload.source.language.toUpperCase(), "EN"); +			payload.source.status_format = defaultValue(payload.source.status_format, "plain"); +			payload.source.sensitive = defaultValue(payload.source.sensitive, false); + +			state.profile = payload; +			// /user/settings only needs a copy of the 'source' obj +			state.settings = { +				source: payload.source +			}; +		}, +		setProfileVal: (state, { payload: [key, val] }) => { +			d.put(state.profile, key, val); +		}, +		setSettingsVal: (state, { payload: [key, val] }) => { +			d.put(state.settings, key, val); +		} +	} +});
\ No newline at end of file diff --git a/web/source/settings-panel/style.css b/web/source/settings-panel/style.css new file mode 100644 index 000000000..35d11fa08 --- /dev/null +++ b/web/source/settings-panel/style.css @@ -0,0 +1,498 @@ +/* +   GoToSocial +   Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + +   This program is free software: you can redistribute it and/or modify +   it under the terms of the GNU Affero General Public License as published by +   the Free Software Foundation, either version 3 of the License, or +   (at your option) any later version. + +   This program is distributed in the hope that it will be useful, +   but WITHOUT ANY WARRANTY; without even the implied warranty of +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +   GNU Affero General Public License for more details. + +   You should have received a copy of the GNU Affero General Public License +   along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +body { +	grid-template-rows: auto 1fr; +} + +.content { +	grid-column: 1 / span 3; /* stretch entire width, to fit panel + sidebar nav */ +} + +section { +	grid-column: 2; +} + +#root { +	display: grid; +	grid-template-columns: 1fr 90ch 1fr; +	width: 100vw; +	max-width: 100vw; +	box-sizing: border-box; + +	section.with-sidebar { +		border-left: none; +		border-top-left-radius: 0; +		border-bottom-left-radius: 0; + +		& > div { +			border-left: 0.2rem solid $border-accent; +			padding-left: 0.4rem; +			display: flex; +			flex-direction: column; +			gap: 0.5rem; +			margin: 2rem 0; + +			h2 { +				margin: 0; +				margin-bottom: 0.5rem; +			} + +			&:only-child { +				border-left: none; +			} + +			&:first-child { +				margin-top: 0; +			} + +			&:last-child { +				margin-bottom: 0; +			} +		} +	} + +	.sidebar { +		align-self: start; +		justify-self: end; +		background: $settings-nav-bg; +		border: $boxshadow-border; +		box-shadow: $boxshadow; +		border-radius: $br; +		border-top-right-radius: 0; +		border-bottom-right-radius: 0; +		display: flex; +		flex-direction: column; +		min-width: 12rem; + +		a { +			text-decoration: none; +		} + +		a:first-child h2 { +			border-top-left-radius: $br; +		} + +		h2 { +			margin: 0; +			padding: 0.5rem; +			font-size: 0.9rem; +			font-weight: bold; +			text-transform: uppercase; +			color: $settings-nav-header-fg; +			background: $settings-nav-header-bg; +		} +		 +		nav { +			display: flex; +			flex-direction: column; + +			a { +				padding: 1rem; +				text-decoration: none; +				transition: 0.1s; +				color: $fg; +	 +				&:hover { +					color: $settings-nav-fg-hover; +					background: $settings-nav-bg-hover; +				} +				 +				&.active { +					color: $settings-nav-fg-active; +					background: $settings-nav-bg-active; +					font-weight: bold; +					text-decoration: underline; +				} +	 +				/* reserve space for bold version of the element, so .active doesn't +					 change container size */ +				&::after {  +					font-weight: bold; +					text-decoration: underline; +					display: block; +					content: attr(data-content); +					height: 1px; +					color: transparent; +					overflow: hidden; +					visibility: hidden; +				} +			} +		} + + +		nav:last-child a:last-child { +			border-bottom-left-radius: $br; +			border-bottom: none; +		} +	} +} + +.capitalize { +	text-transform: capitalize; +} + +section { +	margin-bottom: 1rem; +} + +input, select, textarea { +	box-sizing: border-box; +} + +.error { +	color: $error-fg; +	background: $error-bg; +	border: 0.02rem solid $error-fg; +	border-radius: $br; +	font-weight: bold; +	padding: 0.5rem; +	white-space: pre-wrap; + +	a { +		color: $error-link; +	} + +	pre { +		background: $bg; +		color: $fg; +		padding: 1rem; +		overflow: auto; +		margin: 0; +	} +} + +.hidden { +	display: none; +} + +.messagebutton, .messagebutton > div { +	display: flex; +	align-items: center; +	flex-wrap: wrap; + +	div.padded { +		margin-left: 1rem; +	} + +	button, .button { +		white-space: nowrap; +		margin-right: 1rem; +	} +} + +.messagebutton > div { +	button, .button { +		margin-top: 1rem; +	} +} + +.notImplemented { +	border: 2px solid rgb(70, 79, 88); +	background: repeating-linear-gradient( +		-45deg, +		#525c66, +		#525c66 10px, +		rgb(70, 79, 88) 10px, +		rgb(70, 79, 88) 20px +	) !important; +} + +section.with-sidebar > div { +	display: flex; +	flex-direction: column; +	gap: 1rem; + +	input, textarea { +		width: 100%; +		line-height: 1.5rem; +	} + +	input[type=checkbox] { +		justify-self: start; +		width: initial; +	} + +	input:read-only { +		border: none; +	} + +	input:invalid { +		border-color: red; +	} + +	textarea { +		width: 100%; +	} +	 +	h1 { +		margin-bottom: 0.5rem; +	} +	 +	.moreinfolink { +		font-size: 0.9em; +	} +	 +	.labelinput .border { +		border-radius: 0.2rem; +		border: 0.15rem solid $border_accent; +		padding: 0.3rem; +		display: flex; +		flex-direction: column; +	} +	 +	.file-input.button { +		display: inline-block; +		font-size: 1rem; +		font-weight: normal; +		padding: 0.3rem 0.3rem; +		align-self: flex-start; +		margin-right: 0.2rem; +	} +	 +	.labelinput, .labelselect { +		display: flex; +		flex-direction: column; +		gap: 0.4rem; +	} +	 +	.labelcheckbox { +		display: flex; +		gap: 0.4rem; +	} +	 +	.titlesave { +		display: flex; +		flex-wrap: wrap; +		gap: 0.4rem; +	} +} + +.file-upload > div { +	display: flex; +	gap: 1rem; + +	img { +		height: 8rem; +		border: 0.2rem solid $border-accent; +	} + +	img.avatar { +		width: 8rem; +	} + +	img.header { +		width: 24rem; +	} +} + +.user-profile { +	.overview { +		display: grid; +		grid-template-columns: 70% 30%; + +		.basic { +			margin-top: -4.5rem; + +			.avatar { +				height: 5rem; +				width: 5rem; +			} +	 +			.displayname { +				font-size: 1.3rem; +				padding-top: 0; +				padding-bottom: 0; +				margin-top: 0.7rem; +			} +		} + +		.files { +			width: 100%; +			margin: 1rem; +			margin-right: 0; +			display: flex; +			flex-direction: column; +			justify-content: center; + +			div.form-field { +				width: 100%; +				display: flex; + +				span { +					flex: 1 1 auto; +					overflow: hidden; +					text-overflow: ellipsis; +					white-space: nowrap; +					padding: 0.3rem 0; +				} +			} + +			h3 { +				margin-top: 0; +				margin-bottom: 0.5rem; +			} + +			div:first-child { +				margin-bottom: 1rem; +			} + +			span { +				font-style: italic; +			} +		} +	} +} + +.form-field label { +	font-weight: bold; +} + +.list { +	display: flex; +	flex-direction: column; +	margin-top: 0.5rem; +	max-height: 40rem; +	overflow: auto; + +	.entry { +		display: flex; +		flex-wrap: wrap; +		background: $settings-entry-bg; + +		&:hover { +			background: $settings-entry-hover-bg; +		} +	} +} + +.instance-list { +	.filter { +		display: flex; +		gap: 0.5rem; + +		input { +			width: auto; +			flex: 1 1 auto; +		} +	} + +	.entry { +		padding: 0.3rem; +		margin: 0.2rem 0; + +		#domain { +			flex: 1 1 auto; +			overflow: hidden; +			white-space: nowrap; +			text-overflow: ellipsis; +		} +	} +} + +.bulk h2 { +	display: flex; +	justify-content: space-between; +} + +.emoji-list { +	background: $settings-entry-bg; + +	.entry { +		padding: 0.5rem; +		flex-direction: column; + +		.emoji-group { +			display: flex; +	 +			a { +				border-radius: $br; +				padding: 0.4rem; +				line-height: 0; +	 +				img { +					height: 2rem; +					width: 2rem; +					object-fit: contain; +					vertical-align: middle; +				} + +				&:hover { +					background: $settings-entry-hover-bg; +				} +			} +		} + +		&:hover { +			background: inherit; +		} +	} +} + +.toot { +	padding-top: 0.5rem; +	.contentgrid { +		padding: 0 0.5rem; +	} +} + +@media screen and (max-width: 100ch) { +	#root { +		padding: 1rem; +		grid-template-columns: 100%; +		grid-template-rows: auto auto; + +		.sidebar { +			justify-self: auto; +			margin-bottom: 2rem; +		} + +		.sidebar, section.with-sidebar { +			border-top-left-radius: $br; +			border-top-right-radius: $br; +			border-bottom-left-radius: $br; +			border-bottom-right-radius: $br; +		} + +		.sidebar a:first-child h2 { +			border-top-right-radius: $br; +		} +	} + +	section { +		grid-column: 1; +	} + +	.user-profile .overview { +		grid-template-columns: 100%; +		grid-template-rows: auto auto; + +		.files { +			margin: 0; +			margin-top: 1rem; +		} +	} + +	main section { +		padding: 0.75rem; +	} + +	.instance-list .filter { +		flex-direction: column; +	} +}
\ No newline at end of file diff --git a/web/source/settings-panel/user/profile.js b/web/source/settings-panel/user/profile.js new file mode 100644 index 000000000..7cf3a7b52 --- /dev/null +++ b/web/source/settings-panel/user/profile.js @@ -0,0 +1,113 @@ +/* +	GoToSocial +	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU Affero General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. + +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU Affero General Public License for more details. + +	You should have received a copy of the GNU Affero General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const Promise = require("bluebird"); +const React = require("react"); +const Redux = require("react-redux"); + +const Submit = require("../components/submit"); + +const api = require("../lib/api"); +const user = require("../redux/reducers/user").actions; +const submit = require("../lib/submit"); + +const { formFields } = require("../components/form-fields"); + +const { +	TextInput, +	TextArea, +	Checkbox, +	File +} = formFields(user.setProfileVal, (state) => state.user.profile); + +module.exports = function UserProfile() { +	const dispatch = Redux.useDispatch(); +	const account = Redux.useSelector(state => state.user.profile); +	const instance = Redux.useSelector(state => state.instances.current); + +	const allowCustomCSS = instance.configuration.accounts.allow_custom_css; + +	const [errorMsg, setError] = React.useState(""); +	const [statusMsg, setStatus] = React.useState(""); + +	const saveProfile = submit( +		() => dispatch(api.user.updateProfile()), +		{setStatus, setError} +	); + +	return ( +		<div className="user-profile"> +			<h1>Profile</h1> +			<div className="overview"> +				<div className="profile"> +					<div className="headerimage"> +						<img className="headerpreview" src={account.header} alt={account.header ? `header image for ${account.username}` : "None set"} /> +					</div> +					<div className="basic"> +						<div id="profile-basic-filler2"></div> +						<span className="avatar"><img className="avatarpreview" src={account.avatar} alt={account.avatar ? `avatar image for ${account.username}` : "None set"} /></span> +						<div className="displayname">{account.display_name.trim().length > 0 ? account.display_name : account.username}</div> +						<div className="username"><span>@{account.username}</span></div> +					</div> +				</div> +				<div className="files"> +					<div> +						<h3>Header</h3> +						<File  +							id="header" +							fileType="image/*" +						/> +					</div> +					<div> +						<h3>Avatar</h3> +						<File  +							id="avatar" +							fileType="image/*" +						/> +					</div> +				</div> +			</div> +			<TextInput +				id="display_name" +				name="Name" +				placeHolder="A GoToSocial user" +			/> +			<TextArea +				id="source.note" +				name="Bio" +				placeHolder="Just trying out GoToSocial, my pronouns are they/them and I like sloths." +			/> +			<Checkbox +				id="locked" +				name="Manually approve follow requests? " +			/> +			{ !allowCustomCSS ? null :   +				<TextArea +					id="custom_css" +					name="Custom CSS" +					className="monospace" +				> +					<a href="https://docs.gotosocial.org/en/latest/user_guide/custom_css" target="_blank" className="moreinfolink" rel="noreferrer">Learn more about custom profile CSS (opens in a new tab)</a> +				</TextArea> +			} +			<Submit onClick={saveProfile} label="Save profile info" errorMsg={errorMsg} statusMsg={statusMsg} /> +		</div> +	); +};
\ No newline at end of file diff --git a/web/source/settings-panel/user/settings.js b/web/source/settings-panel/user/settings.js new file mode 100644 index 000000000..ccb3e911d --- /dev/null +++ b/web/source/settings-panel/user/settings.js @@ -0,0 +1,140 @@ +/* +	GoToSocial +	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org + +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU Affero General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. + +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU Affero General Public License for more details. + +	You should have received a copy of the GNU Affero General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const Promise = require("bluebird"); +const React = require("react"); +const Redux = require("react-redux"); + +const api = require("../lib/api"); +const user = require("../redux/reducers/user").actions; +const submit = require("../lib/submit"); + +const Languages = require("../components/languages"); +const Submit = require("../components/submit"); + +const { +	Checkbox, +	Select, +} = require("../components/form-fields").formFields(user.setSettingsVal, (state) => state.user.settings); + +module.exports = function UserSettings() { +	const dispatch = Redux.useDispatch(); + +	const [errorMsg, setError] = React.useState(""); +	const [statusMsg, setStatus] = React.useState(""); + +	const updateSettings = submit( +		() => dispatch(api.user.updateSettings()), +		{setStatus, setError} +	); + +	return ( +		<> +			<div className="user-settings"> +				<h1>Post settings</h1> +				<Select id="source.language" name="Default post language" options={ +					<Languages/> +				}> +				</Select> +				<Select id="source.privacy" name="Default post privacy" options={ +					<> +						<option value="private">Private / followers-only</option> +						<option value="unlisted">Unlisted</option> +						<option value="public">Public</option> +					</> +				}> +					<a href="https://docs.gotosocial.org/en/latest/user_guide/posts/#privacy-settings" target="_blank" className="moreinfolink" rel="noreferrer">Learn more about post privacy settings (opens in a new tab)</a> +				</Select> +				<Select id="source.status_format" name="Default post format" options={ +					<> +						<option value="plain">Plain (default)</option> +						<option value="markdown">Markdown</option> +					</> +				}> +					<a href="https://docs.gotosocial.org/en/latest/user_guide/posts/#input-types" target="_blank" className="moreinfolink" rel="noreferrer">Learn more about post format settings (opens in a new tab)</a> +				</Select> +				<Checkbox +					id="source.sensitive" +					name="Mark my posts as sensitive by default" +				/> + +				<Submit onClick={updateSettings} label="Save post settings" errorMsg={errorMsg} statusMsg={statusMsg}/> +			</div> +			<div> +				<PasswordChange/> +			</div> +		</> +	); +}; + +function PasswordChange() { +	const dispatch = Redux.useDispatch(); + +	const [errorMsg, setError] = React.useState(""); +	const [statusMsg, setStatus] = React.useState(""); + +	const [oldPassword, setOldPassword] = React.useState(""); +	const [newPassword, setNewPassword] = React.useState(""); +	const [newPasswordConfirm, setNewPasswordConfirm] = React.useState(""); + +	function changePassword() { +		if (newPassword !== newPasswordConfirm) { +			setError("New password and confirm new password did not match!"); +			return; +		} +		 +		setStatus("PATCHing"); +		setError(""); +		return Promise.try(() => { +			let data = { +				old_password: oldPassword, +				new_password: newPassword +			}; +			return dispatch(api.apiCall("POST", "/api/v1/user/password_change", data, "form")); +		}).then(() => { +			setStatus("Saved!"); +			setOldPassword(""); +			setNewPassword(""); +			setNewPasswordConfirm(""); +		}).catch((e) => { +			setError(e.message); +			setStatus(""); +		}); +	} + +	return ( +		<> +			<h1>Change password</h1> +			<div className="labelinput"> +				<label htmlFor="password">Current password</label> +				<input name="password" id="password" type="password" autoComplete="current-password" value={oldPassword} onChange={(e) => setOldPassword(e.target.value)} /> +			</div> +			<div className="labelinput"> +				<label htmlFor="new-password">New password</label> +				<input name="new-password" id="new-password" type="password" autoComplete="new-password" value={newPassword} onChange={(e) => setNewPassword(e.target.value)} /> +			</div> +			<div className="labelinput"> +				<label htmlFor="confirm-new-password">Confirm new password</label> +				<input name="confirm-new-password" id="confirm-new-password" type="password" autoComplete="new-password" value={newPasswordConfirm} onChange={(e) => setNewPasswordConfirm(e.target.value)} /> +			</div> +			<Submit onClick={changePassword} label="Save new password" errorMsg={errorMsg} statusMsg={statusMsg}/> +		</> +	); +}
\ No newline at end of file diff --git a/web/source/yarn.lock b/web/source/yarn.lock index 08ab095bf..830ca5055 100644 --- a/web/source/yarn.lock +++ b/web/source/yarn.lock @@ -24,38 +24,38 @@    dependencies:      "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8": -  version "7.18.8" -  resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.8.tgz#2483f565faca607b8535590e84e7de323f27764d" -  integrity sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ== +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8", "@babel/compat-data@^7.19.1": +  version "7.19.1" +  resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.19.1.tgz#72d647b4ff6a4f82878d184613353af1dd0290f9" +  integrity sha512-72a9ghR0gnESIa7jBN53U32FOVCEoztyIlKaNoU05zRhEecduGK9L9c3ww7Mp06JiR+0ls0GBPFJQwwtjn9ksg==  "@babel/core@^7.12.13": -  version "7.18.10" -  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.10.tgz#39ad504991d77f1f3da91be0b8b949a5bc466fb8" -  integrity sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw== +  version "7.19.1" +  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.19.1.tgz#c8fa615c5e88e272564ace3d42fbc8b17bfeb22b" +  integrity sha512-1H8VgqXme4UXCRv7/Wa1bq7RVymKOzC7znjyFM8KiEzwFqcKUKYNoQef4GhdklgNvoBXyW4gYhuBNCM5o1zImw==    dependencies:      "@ampproject/remapping" "^2.1.0"      "@babel/code-frame" "^7.18.6" -    "@babel/generator" "^7.18.10" -    "@babel/helper-compilation-targets" "^7.18.9" -    "@babel/helper-module-transforms" "^7.18.9" -    "@babel/helpers" "^7.18.9" -    "@babel/parser" "^7.18.10" +    "@babel/generator" "^7.19.0" +    "@babel/helper-compilation-targets" "^7.19.1" +    "@babel/helper-module-transforms" "^7.19.0" +    "@babel/helpers" "^7.19.0" +    "@babel/parser" "^7.19.1"      "@babel/template" "^7.18.10" -    "@babel/traverse" "^7.18.10" -    "@babel/types" "^7.18.10" +    "@babel/traverse" "^7.19.1" +    "@babel/types" "^7.19.0"      convert-source-map "^1.7.0"      debug "^4.1.0"      gensync "^1.0.0-beta.2"      json5 "^2.2.1"      semver "^6.3.0" -"@babel/generator@^7.18.10": -  version "7.18.12" -  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.12.tgz#fa58daa303757bd6f5e4bbca91b342040463d9f4" -  integrity sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg== +"@babel/generator@^7.19.0": +  version "7.19.0" +  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.19.0.tgz#785596c06425e59334df2ccee63ab166b738419a" +  integrity sha512-S1ahxf1gZ2dpoiFgA+ohK9DIpz50bJ0CWs7Zlzb54Z4sG8qmdIrGrVqmy1sAtTVRb+9CU6U8VqT9L0Zj7hxHVg==    dependencies: -    "@babel/types" "^7.18.10" +    "@babel/types" "^7.19.0"      "@jridgewell/gen-mapping" "^0.3.2"      jsesc "^2.5.1" @@ -74,41 +74,41 @@      "@babel/helper-explode-assignable-expression" "^7.18.6"      "@babel/types" "^7.18.9" -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9": -  version "7.18.9" -  resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz#69e64f57b524cde3e5ff6cc5a9f4a387ee5563bf" -  integrity sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg== +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.19.0", "@babel/helper-compilation-targets@^7.19.1": +  version "7.19.1" +  resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.1.tgz#7f630911d83b408b76fe584831c98e5395d7a17c" +  integrity sha512-LlLkkqhCMyz2lkQPvJNdIYU7O5YjWRgC2R4omjCTpZd8u8KMQzZvX4qce+/BluN1rcQiV7BoGUpmQ0LeHerbhg==    dependencies: -    "@babel/compat-data" "^7.18.8" +    "@babel/compat-data" "^7.19.1"      "@babel/helper-validator-option" "^7.18.6" -    browserslist "^4.20.2" +    browserslist "^4.21.3"      semver "^6.3.0"  "@babel/helper-create-class-features-plugin@^7.18.6": -  version "7.18.9" -  resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.9.tgz#d802ee16a64a9e824fcbf0a2ffc92f19d58550ce" -  integrity sha512-WvypNAYaVh23QcjpMR24CwZY2Nz6hqdOcFdPbNpV56hL5H6KiFheO7Xm1aPdlLQ7d5emYZX7VZwPp9x3z+2opw== +  version "7.19.0" +  resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.19.0.tgz#bfd6904620df4e46470bae4850d66be1054c404b" +  integrity sha512-NRz8DwF4jT3UfrmUoZjd0Uph9HQnP30t7Ash+weACcyNkiYTywpIjDBgReJMKgr+n86sn2nPVVmJ28Dm053Kqw==    dependencies:      "@babel/helper-annotate-as-pure" "^7.18.6"      "@babel/helper-environment-visitor" "^7.18.9" -    "@babel/helper-function-name" "^7.18.9" +    "@babel/helper-function-name" "^7.19.0"      "@babel/helper-member-expression-to-functions" "^7.18.9"      "@babel/helper-optimise-call-expression" "^7.18.6"      "@babel/helper-replace-supers" "^7.18.9"      "@babel/helper-split-export-declaration" "^7.18.6" -"@babel/helper-create-regexp-features-plugin@^7.18.6": -  version "7.18.6" -  resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.18.6.tgz#3e35f4e04acbbf25f1b3534a657610a000543d3c" -  integrity sha512-7LcpH1wnQLGrI+4v+nPp+zUvIkF9x0ddv1Hkdue10tg3gmRnLy97DXh4STiOf1qeIInyD69Qv5kKSZzKD8B/7A== +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.19.0": +  version "7.19.0" +  resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz#7976aca61c0984202baca73d84e2337a5424a41b" +  integrity sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw==    dependencies:      "@babel/helper-annotate-as-pure" "^7.18.6"      regexpu-core "^5.1.0" -"@babel/helper-define-polyfill-provider@^0.3.2": -  version "0.3.2" -  resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.2.tgz#bd10d0aca18e8ce012755395b05a79f45eca5073" -  integrity sha512-r9QJJ+uDWrd+94BSPcP6/de67ygLtvVy6cK4luE6MOuDsZIdoaPBnfSpbO/+LTifjPckbKXRuI9BB/Z2/y3iTg== +"@babel/helper-define-polyfill-provider@^0.3.3": +  version "0.3.3" +  resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz#8612e55be5d51f0cd1f36b4a5a83924e89884b7a" +  integrity sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==    dependencies:      "@babel/helper-compilation-targets" "^7.17.7"      "@babel/helper-plugin-utils" "^7.16.7" @@ -129,13 +129,13 @@    dependencies:      "@babel/types" "^7.18.6" -"@babel/helper-function-name@^7.18.9": -  version "7.18.9" -  resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz#940e6084a55dee867d33b4e487da2676365e86b0" -  integrity sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A== +"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0": +  version "7.19.0" +  resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" +  integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==    dependencies: -    "@babel/template" "^7.18.6" -    "@babel/types" "^7.18.9" +    "@babel/template" "^7.18.10" +    "@babel/types" "^7.19.0"  "@babel/helper-hoist-variables@^7.18.6":    version "7.18.6" @@ -158,19 +158,19 @@    dependencies:      "@babel/types" "^7.18.6" -"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.18.9": -  version "7.18.9" -  resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz#5a1079c005135ed627442df31a42887e80fcb712" -  integrity sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g== +"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.19.0": +  version "7.19.0" +  resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz#309b230f04e22c58c6a2c0c0c7e50b216d350c30" +  integrity sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==    dependencies:      "@babel/helper-environment-visitor" "^7.18.9"      "@babel/helper-module-imports" "^7.18.6"      "@babel/helper-simple-access" "^7.18.6"      "@babel/helper-split-export-declaration" "^7.18.6"      "@babel/helper-validator-identifier" "^7.18.6" -    "@babel/template" "^7.18.6" -    "@babel/traverse" "^7.18.9" -    "@babel/types" "^7.18.9" +    "@babel/template" "^7.18.10" +    "@babel/traverse" "^7.19.0" +    "@babel/types" "^7.19.0"  "@babel/helper-optimise-call-expression@^7.18.6":    version "7.18.6" @@ -179,10 +179,10 @@    dependencies:      "@babel/types" "^7.18.6" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": -  version "7.18.9" -  resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz#4b8aea3b069d8cb8a72cdfe28ddf5ceca695ef2f" -  integrity sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w== +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": +  version "7.19.0" +  resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz#4796bb14961521f0f8715990bee2fb6e51ce21bf" +  integrity sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==  "@babel/helper-remap-async-to-generator@^7.18.6", "@babel/helper-remap-async-to-generator@^7.18.9":    version "7.18.9" @@ -195,15 +195,15 @@      "@babel/types" "^7.18.9"  "@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.18.9": -  version "7.18.9" -  resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.18.9.tgz#1092e002feca980fbbb0bd4d51b74a65c6a500e6" -  integrity sha512-dNsWibVI4lNT6HiuOIBr1oyxo40HvIVmbwPUm3XZ7wMh4k2WxrxTqZwSqw/eEmXDS9np0ey5M2bz9tBmO9c+YQ== +  version "7.19.1" +  resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.19.1.tgz#e1592a9b4b368aa6bdb8784a711e0bcbf0612b78" +  integrity sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw==    dependencies:      "@babel/helper-environment-visitor" "^7.18.9"      "@babel/helper-member-expression-to-functions" "^7.18.9"      "@babel/helper-optimise-call-expression" "^7.18.6" -    "@babel/traverse" "^7.18.9" -    "@babel/types" "^7.18.9" +    "@babel/traverse" "^7.19.1" +    "@babel/types" "^7.19.0"  "@babel/helper-simple-access@^7.18.6":    version "7.18.6" @@ -232,9 +232,9 @@    integrity sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==  "@babel/helper-validator-identifier@^7.18.6": -  version "7.18.6" -  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076" -  integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g== +  version "7.19.1" +  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" +  integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==  "@babel/helper-validator-option@^7.18.6":    version "7.18.6" @@ -242,23 +242,23 @@    integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==  "@babel/helper-wrap-function@^7.18.9": -  version "7.18.11" -  resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.18.11.tgz#bff23ace436e3f6aefb61f85ffae2291c80ed1fb" -  integrity sha512-oBUlbv+rjZLh2Ks9SKi4aL7eKaAXBWleHzU89mP0G6BMUlRxSckk9tSIkgDGydhgFxHuGSlBQZfnaD47oBEB7w== +  version "7.19.0" +  resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.19.0.tgz#89f18335cff1152373222f76a4b37799636ae8b1" +  integrity sha512-txX8aN8CZyYGTwcLhlk87KRqncAzhh5TpQamZUa0/u3an36NtDpUP6bQgBCBcLeBs09R/OwQu3OjK0k/HwfNDg==    dependencies: -    "@babel/helper-function-name" "^7.18.9" +    "@babel/helper-function-name" "^7.19.0"      "@babel/template" "^7.18.10" -    "@babel/traverse" "^7.18.11" -    "@babel/types" "^7.18.10" +    "@babel/traverse" "^7.19.0" +    "@babel/types" "^7.19.0" -"@babel/helpers@^7.18.9": -  version "7.18.9" -  resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.9.tgz#4bef3b893f253a1eced04516824ede94dcfe7ff9" -  integrity sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ== +"@babel/helpers@^7.19.0": +  version "7.19.0" +  resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.19.0.tgz#f30534657faf246ae96551d88dd31e9d1fa1fc18" +  integrity sha512-DRBCKGwIEdqY3+rPJgG/dKfQy9+08rHIAJx8q2p+HSWP87s2HCrQmaAMMyMll2kIXKCW0cO1RdQskx15Xakftg==    dependencies: -    "@babel/template" "^7.18.6" -    "@babel/traverse" "^7.18.9" -    "@babel/types" "^7.18.9" +    "@babel/template" "^7.18.10" +    "@babel/traverse" "^7.19.0" +    "@babel/types" "^7.19.0"  "@babel/highlight@^7.10.4", "@babel/highlight@^7.18.6":    version "7.18.6" @@ -269,10 +269,10 @@      chalk "^2.0.0"      js-tokens "^4.0.0" -"@babel/parser@^7.18.10", "@babel/parser@^7.18.11": -  version "7.18.11" -  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.11.tgz#68bb07ab3d380affa9a3f96728df07969645d2d9" -  integrity sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ== +"@babel/parser@^7.18.10", "@babel/parser@^7.19.1": +  version "7.19.1" +  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.1.tgz#6f6d6c2e621aad19a92544cc217ed13f1aac5b4c" +  integrity sha512-h7RCSorm1DdTVGJf3P2Mhj3kdnkmF/EiysUkzS2TdgAYqyjFdMQJbVuXOBej2SBJaXan/lIVtT6KkGbyyq753A==  "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6":    version "7.18.6" @@ -290,13 +290,13 @@      "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9"      "@babel/plugin-proposal-optional-chaining" "^7.18.9" -"@babel/plugin-proposal-async-generator-functions@^7.18.10": -  version "7.18.10" -  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.10.tgz#85ea478c98b0095c3e4102bff3b67d306ed24952" -  integrity sha512-1mFuY2TOsR1hxbjCo4QL+qlIjV07p4H4EUYw2J/WCqsvFV6V9X9z9YhXbWndc/4fw+hYGlDT7egYxliMp5O6Ew== +"@babel/plugin-proposal-async-generator-functions@^7.19.1": +  version "7.19.1" +  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.19.1.tgz#34f6f5174b688529342288cd264f80c9ea9fb4a7" +  integrity sha512-0yu8vNATgLy4ivqMNBIwb1HebCelqN7YX8SL3FDXORv/RqT0zEEWUCH4GH44JsSrvCu6GqnAdR5EBFAPeNBB4Q==    dependencies:      "@babel/helper-environment-visitor" "^7.18.9" -    "@babel/helper-plugin-utils" "^7.18.9" +    "@babel/helper-plugin-utils" "^7.19.0"      "@babel/helper-remap-async-to-generator" "^7.18.9"      "@babel/plugin-syntax-async-generators" "^7.8.4" @@ -561,16 +561,17 @@    dependencies:      "@babel/helper-plugin-utils" "^7.18.9" -"@babel/plugin-transform-classes@^7.18.9": -  version "7.18.9" -  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.9.tgz#90818efc5b9746879b869d5ce83eb2aa48bbc3da" -  integrity sha512-EkRQxsxoytpTlKJmSPYrsOMjCILacAjtSVkd4gChEe2kXjFCun3yohhW5I7plXJhCemM0gKsaGMcO8tinvCA5g== +"@babel/plugin-transform-classes@^7.19.0": +  version "7.19.0" +  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.19.0.tgz#0e61ec257fba409c41372175e7c1e606dc79bb20" +  integrity sha512-YfeEE9kCjqTS9IitkgfJuxjcEtLUHMqa8yUJ6zdz8vR7hKuo6mOy2C05P0F1tdMmDCeuyidKnlrw/iTppHcr2A==    dependencies:      "@babel/helper-annotate-as-pure" "^7.18.6" +    "@babel/helper-compilation-targets" "^7.19.0"      "@babel/helper-environment-visitor" "^7.18.9" -    "@babel/helper-function-name" "^7.18.9" +    "@babel/helper-function-name" "^7.19.0"      "@babel/helper-optimise-call-expression" "^7.18.6" -    "@babel/helper-plugin-utils" "^7.18.9" +    "@babel/helper-plugin-utils" "^7.19.0"      "@babel/helper-replace-supers" "^7.18.9"      "@babel/helper-split-export-declaration" "^7.18.6"      globals "^11.1.0" @@ -582,10 +583,10 @@    dependencies:      "@babel/helper-plugin-utils" "^7.18.9" -"@babel/plugin-transform-destructuring@^7.18.9": -  version "7.18.9" -  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.9.tgz#68906549c021cb231bee1db21d3b5b095f8ee292" -  integrity sha512-p5VCYNddPLkZTq4XymQIaIfZNJwT9YsjkPOhkVEqt6QIpQFZVM9IltqqYpOEkJoN1DPznmxUDyZ5CTZs/ZCuHA== +"@babel/plugin-transform-destructuring@^7.18.13": +  version "7.18.13" +  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.13.tgz#9e03bc4a94475d62b7f4114938e6c5c33372cbf5" +  integrity sha512-TodpQ29XekIsex2A+YJPj5ax2plkGa8YYY6mFjCohk/IG9IY42Rtuj1FuDeemfg2ipxIFLzPeA83SIBnlhSIow==    dependencies:      "@babel/helper-plugin-utils" "^7.18.9" @@ -661,14 +662,14 @@      "@babel/helper-simple-access" "^7.18.6"      babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-systemjs@^7.18.9": -  version "7.18.9" -  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.9.tgz#545df284a7ac6a05125e3e405e536c5853099a06" -  integrity sha512-zY/VSIbbqtoRoJKo2cDTewL364jSlZGvn0LKOf9ntbfxOvjfmyrdtEEOAdswOswhZEb8UH3jDkCKHd1sPgsS0A== +"@babel/plugin-transform-modules-systemjs@^7.19.0": +  version "7.19.0" +  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.0.tgz#5f20b471284430f02d9c5059d9b9a16d4b085a1f" +  integrity sha512-x9aiR0WXAWmOWsqcsnrzGR+ieaTMVyGyffPVA7F8cXAGt/UxefYv6uSHZLkAFChN5M5Iy1+wjE+xJuPt22H39A==    dependencies:      "@babel/helper-hoist-variables" "^7.18.6" -    "@babel/helper-module-transforms" "^7.18.9" -    "@babel/helper-plugin-utils" "^7.18.9" +    "@babel/helper-module-transforms" "^7.19.0" +    "@babel/helper-plugin-utils" "^7.19.0"      "@babel/helper-validator-identifier" "^7.18.6"      babel-plugin-dynamic-import-node "^2.3.3" @@ -680,13 +681,13 @@      "@babel/helper-module-transforms" "^7.18.6"      "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-named-capturing-groups-regex@^7.18.6": -  version "7.18.6" -  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.18.6.tgz#c89bfbc7cc6805d692f3a49bc5fc1b630007246d" -  integrity sha512-UmEOGF8XgaIqD74bC8g7iV3RYj8lMf0Bw7NJzvnS9qQhM4mg+1WHKotUIdjxgD2RGrgFLZZPCFPFj3P/kVDYhg== +"@babel/plugin-transform-named-capturing-groups-regex@^7.19.1": +  version "7.19.1" +  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.19.1.tgz#ec7455bab6cd8fb05c525a94876f435a48128888" +  integrity sha512-oWk9l9WItWBQYS4FgXD4Uyy5kq898lvkXpXQxoJEY1RnvPk4R/Dvu2ebXU9q8lP+rlMwUQTFf2Ok6d78ODa0kw==    dependencies: -    "@babel/helper-create-regexp-features-plugin" "^7.18.6" -    "@babel/helper-plugin-utils" "^7.18.6" +    "@babel/helper-create-regexp-features-plugin" "^7.19.0" +    "@babel/helper-plugin-utils" "^7.19.0"  "@babel/plugin-transform-new-target@^7.18.6":    version "7.18.6" @@ -732,15 +733,15 @@      "@babel/plugin-transform-react-jsx" "^7.18.6"  "@babel/plugin-transform-react-jsx@^7.18.6": -  version "7.18.10" -  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.18.10.tgz#ea47b2c4197102c196cbd10db9b3bb20daa820f1" -  integrity sha512-gCy7Iikrpu3IZjYZolFE4M1Sm+nrh1/6za2Ewj77Z+XirT4TsbJcvOFOyF+fRPwU6AKKK136CZxx6L8AbSFG6A== +  version "7.19.0" +  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.19.0.tgz#b3cbb7c3a00b92ec8ae1027910e331ba5c500eb9" +  integrity sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg==    dependencies:      "@babel/helper-annotate-as-pure" "^7.18.6"      "@babel/helper-module-imports" "^7.18.6" -    "@babel/helper-plugin-utils" "^7.18.9" +    "@babel/helper-plugin-utils" "^7.19.0"      "@babel/plugin-syntax-jsx" "^7.18.6" -    "@babel/types" "^7.18.10" +    "@babel/types" "^7.19.0"  "@babel/plugin-transform-react-pure-annotations@^7.18.6":    version "7.18.6" @@ -772,12 +773,12 @@    dependencies:      "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-spread@^7.18.9": -  version "7.18.9" -  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.18.9.tgz#6ea7a6297740f381c540ac56caf75b05b74fb664" -  integrity sha512-39Q814wyoOPtIB/qGopNIL9xDChOE1pNU0ZY5dO0owhiVt/5kFm4li+/bBtwc7QotG0u5EPzqhZdjMtmqBqyQA== +"@babel/plugin-transform-spread@^7.19.0": +  version "7.19.0" +  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz#dd60b4620c2fec806d60cfaae364ec2188d593b6" +  integrity sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w==    dependencies: -    "@babel/helper-plugin-utils" "^7.18.9" +    "@babel/helper-plugin-utils" "^7.19.0"      "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9"  "@babel/plugin-transform-sticky-regex@^7.18.6": @@ -817,17 +818,17 @@      "@babel/helper-plugin-utils" "^7.18.6"  "@babel/preset-env@^7.12.13": -  version "7.18.10" -  resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.18.10.tgz#83b8dfe70d7eea1aae5a10635ab0a5fe60dfc0f4" -  integrity sha512-wVxs1yjFdW3Z/XkNfXKoblxoHgbtUF7/l3PvvP4m02Qz9TZ6uZGxRVYjSQeR87oQmHco9zWitW5J82DJ7sCjvA== +  version "7.19.1" +  resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.19.1.tgz#9f04c916f9c0205a48ebe5cc1be7768eb1983f67" +  integrity sha512-c8B2c6D16Lp+Nt6HcD+nHl0VbPKVnNPTpszahuxJJnurfMtKeZ80A+qUv48Y7wqvS+dTFuLuaM9oYxyNHbCLWA==    dependencies: -    "@babel/compat-data" "^7.18.8" -    "@babel/helper-compilation-targets" "^7.18.9" -    "@babel/helper-plugin-utils" "^7.18.9" +    "@babel/compat-data" "^7.19.1" +    "@babel/helper-compilation-targets" "^7.19.1" +    "@babel/helper-plugin-utils" "^7.19.0"      "@babel/helper-validator-option" "^7.18.6"      "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6"      "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.18.9" -    "@babel/plugin-proposal-async-generator-functions" "^7.18.10" +    "@babel/plugin-proposal-async-generator-functions" "^7.19.1"      "@babel/plugin-proposal-class-properties" "^7.18.6"      "@babel/plugin-proposal-class-static-block" "^7.18.6"      "@babel/plugin-proposal-dynamic-import" "^7.18.6" @@ -861,9 +862,9 @@      "@babel/plugin-transform-async-to-generator" "^7.18.6"      "@babel/plugin-transform-block-scoped-functions" "^7.18.6"      "@babel/plugin-transform-block-scoping" "^7.18.9" -    "@babel/plugin-transform-classes" "^7.18.9" +    "@babel/plugin-transform-classes" "^7.19.0"      "@babel/plugin-transform-computed-properties" "^7.18.9" -    "@babel/plugin-transform-destructuring" "^7.18.9" +    "@babel/plugin-transform-destructuring" "^7.18.13"      "@babel/plugin-transform-dotall-regex" "^7.18.6"      "@babel/plugin-transform-duplicate-keys" "^7.18.9"      "@babel/plugin-transform-exponentiation-operator" "^7.18.6" @@ -873,9 +874,9 @@      "@babel/plugin-transform-member-expression-literals" "^7.18.6"      "@babel/plugin-transform-modules-amd" "^7.18.6"      "@babel/plugin-transform-modules-commonjs" "^7.18.6" -    "@babel/plugin-transform-modules-systemjs" "^7.18.9" +    "@babel/plugin-transform-modules-systemjs" "^7.19.0"      "@babel/plugin-transform-modules-umd" "^7.18.6" -    "@babel/plugin-transform-named-capturing-groups-regex" "^7.18.6" +    "@babel/plugin-transform-named-capturing-groups-regex" "^7.19.1"      "@babel/plugin-transform-new-target" "^7.18.6"      "@babel/plugin-transform-object-super" "^7.18.6"      "@babel/plugin-transform-parameters" "^7.18.8" @@ -883,18 +884,18 @@      "@babel/plugin-transform-regenerator" "^7.18.6"      "@babel/plugin-transform-reserved-words" "^7.18.6"      "@babel/plugin-transform-shorthand-properties" "^7.18.6" -    "@babel/plugin-transform-spread" "^7.18.9" +    "@babel/plugin-transform-spread" "^7.19.0"      "@babel/plugin-transform-sticky-regex" "^7.18.6"      "@babel/plugin-transform-template-literals" "^7.18.9"      "@babel/plugin-transform-typeof-symbol" "^7.18.9"      "@babel/plugin-transform-unicode-escapes" "^7.18.10"      "@babel/plugin-transform-unicode-regex" "^7.18.6"      "@babel/preset-modules" "^0.1.5" -    "@babel/types" "^7.18.10" -    babel-plugin-polyfill-corejs2 "^0.3.2" -    babel-plugin-polyfill-corejs3 "^0.5.3" -    babel-plugin-polyfill-regenerator "^0.4.0" -    core-js-compat "^3.22.1" +    "@babel/types" "^7.19.0" +    babel-plugin-polyfill-corejs2 "^0.3.3" +    babel-plugin-polyfill-corejs3 "^0.6.0" +    babel-plugin-polyfill-regenerator "^0.4.1" +    core-js-compat "^3.25.1"      semver "^6.3.0"  "@babel/preset-modules@^0.1.5": @@ -920,14 +921,14 @@      "@babel/plugin-transform-react-jsx-development" "^7.18.6"      "@babel/plugin-transform-react-pure-annotations" "^7.18.6" -"@babel/runtime@^7.8.4": -  version "7.18.9" -  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a" -  integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw== +"@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": +  version "7.19.0" +  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259" +  integrity sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==    dependencies:      regenerator-runtime "^0.13.4" -"@babel/template@^7.18.10", "@babel/template@^7.18.6": +"@babel/template@^7.18.10":    version "7.18.10"    resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71"    integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA== @@ -936,26 +937,26 @@      "@babel/parser" "^7.18.10"      "@babel/types" "^7.18.10" -"@babel/traverse@^7.18.10", "@babel/traverse@^7.18.11", "@babel/traverse@^7.18.9": -  version "7.18.11" -  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.11.tgz#3d51f2afbd83ecf9912bcbb5c4d94e3d2ddaa16f" -  integrity sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ== +"@babel/traverse@^7.19.0", "@babel/traverse@^7.19.1": +  version "7.19.1" +  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.19.1.tgz#0fafe100a8c2a603b4718b1d9bf2568d1d193347" +  integrity sha512-0j/ZfZMxKukDaag2PtOPDbwuELqIar6lLskVPPJDjXMXjfLb1Obo/1yjxIGqqAJrmfaTIY3z2wFLAQ7qSkLsuA==    dependencies:      "@babel/code-frame" "^7.18.6" -    "@babel/generator" "^7.18.10" +    "@babel/generator" "^7.19.0"      "@babel/helper-environment-visitor" "^7.18.9" -    "@babel/helper-function-name" "^7.18.9" +    "@babel/helper-function-name" "^7.19.0"      "@babel/helper-hoist-variables" "^7.18.6"      "@babel/helper-split-export-declaration" "^7.18.6" -    "@babel/parser" "^7.18.11" -    "@babel/types" "^7.18.10" +    "@babel/parser" "^7.19.1" +    "@babel/types" "^7.19.0"      debug "^4.1.0"      globals "^11.1.0" -"@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.4.4": -  version "7.18.10" -  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.10.tgz#4908e81b6b339ca7c6b7a555a5fc29446f26dde6" -  integrity sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ== +"@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.4.4": +  version "7.19.0" +  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.19.0.tgz#75f21d73d73dc0351f3368d28db73465f4814600" +  integrity sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA==    dependencies:      "@babel/helper-string-parser" "^7.18.10"      "@babel/helper-validator-identifier" "^7.18.6" @@ -981,6 +982,23 @@      minimatch "^3.0.4"      strip-json-comments "^3.1.1" +"@f0x52/budo-express@^1.1.0": +  version "1.1.0" +  resolved "https://registry.yarnpkg.com/@f0x52/budo-express/-/budo-express-1.1.0.tgz#dbda0eab9da0c186fbb12471d14a64331e474b57" +  integrity sha512-BKZOCJW32fIGehD7n7uCBlm8j5BDYQIE/xDFW86QabnnEHir4Q4/jC/XWyXfrgTHI6RjyySUZGp/kBT+tCBzsA== +  dependencies: +    assure-array "^1.0.0" +    bluebird "^3.7.2" +    browserify "^16.5.0" +    budo "^11.5.0" +    chalk "^3.0.0" +    default-value "^1.0.0" +    entities "^2.0.0" +    inject-lr-script "^2.2.0" +    is-stream "^2.0.0" +    stacked "^1.1.1" +    validatem "^0.2.0" +  "@f0x52/eslint-config-react@^1.1.0":    version "1.1.0"    resolved "https://registry.yarnpkg.com/@f0x52/eslint-config-react/-/eslint-config-react-1.1.0.tgz#734973a7627603b19e3cec2620658a25d405b110" @@ -1012,9 +1030,9 @@    integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==  "@joepie91/eslint-config@^1.1.0": -  version "1.1.0" -  resolved "https://registry.yarnpkg.com/@joepie91/eslint-config/-/eslint-config-1.1.0.tgz#9397e6ce0a010cb57dcf8aef8754d3a5ce0ae36a" -  integrity sha512-XliasRSUfOz1/bAvTBaUlCjWDbceCW4y1DnvFfW7Yw9p2FbNRR0w8WoPdTxTCjKuoZ7/OQMeBxIe2y9Qy6rbYw== +  version "1.1.1" +  resolved "https://registry.yarnpkg.com/@joepie91/eslint-config/-/eslint-config-1.1.1.tgz#cb276dec6dd25b5777daefbef561850c9717180d" +  integrity sha512-q8l83tdpL0YGC24ftlpeHgmQIIRmcpiVhwwEUFPcJ1YXWaee/JjoUs6e5tLKMTNNk+fvDKtq2YPSXkmLQU7h5Q==  "@jridgewell/gen-mapping@^0.1.0":    version "0.1.1" @@ -1049,13 +1067,55 @@    integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==  "@jridgewell/trace-mapping@^0.3.9": -  version "0.3.14" -  resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed" -  integrity sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ== +  version "0.3.15" +  resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz#aba35c48a38d3fd84b37e66c9c0423f9744f9774" +  integrity sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==    dependencies:      "@jridgewell/resolve-uri" "^3.0.3"      "@jridgewell/sourcemap-codec" "^1.4.10" +"@reduxjs/toolkit@^1.8.5": +  version "1.8.5" +  resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.8.5.tgz#c14bece03ee08be88467f22dc0ecf9cf875527cd" +  integrity sha512-f4D5EXO7A7Xq35T0zRbWq5kJQyXzzscnHKmjnu2+37B3rwHU6mX9PYlbfXdnxcY6P/7zfmjhgan0Z+yuOfeBmA== +  dependencies: +    immer "^9.0.7" +    redux "^4.1.2" +    redux-thunk "^2.4.1" +    reselect "^4.1.5" + +"@types/hoist-non-react-statics@^3.3.1": +  version "3.3.1" +  resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" +  integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== +  dependencies: +    "@types/react" "*" +    hoist-non-react-statics "^3.3.0" + +"@types/prop-types@*": +  version "15.7.5" +  resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" +  integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== + +"@types/react@*": +  version "18.0.20" +  resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.20.tgz#e4c36be3a55eb5b456ecf501bd4a00fd4fd0c9ab" +  integrity sha512-MWul1teSPxujEHVwZl4a5HxQ9vVNsjTchVA+xRqv/VYGCuKGAU6UhfrTdF5aBefwD1BHUD8i/zq+O/vyCm/FrA== +  dependencies: +    "@types/prop-types" "*" +    "@types/scheduler" "*" +    csstype "^3.0.2" + +"@types/scheduler@*": +  version "0.16.2" +  resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" +  integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== + +"@types/use-sync-external-store@^0.0.3": +  version "0.0.3" +  resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43" +  integrity sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA== +  "@validatem/allow-extra-properties@^0.1.0":    version "0.1.0"    resolved "https://registry.yarnpkg.com/@validatem/allow-extra-properties/-/allow-extra-properties-0.1.0.tgz#e8c434818d6fd74b8cb237cfaa4d548295de13c1" @@ -1345,11 +1405,6 @@ acorn-walk@^7.0.0:    resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"    integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== -acorn@^5.2.1: -  version "5.7.4" -  resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e" -  integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg== -  acorn@^7.0.0, acorn@^7.4.0:    version "7.4.1"    resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" @@ -1444,21 +1499,6 @@ argparse@^1.0.7:    dependencies:      sprintf-js "~1.0.2" -arr-diff@^4.0.0: -  version "4.0.0" -  resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" -  integrity sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA== - -arr-flatten@^1.1.0: -  version "1.1.0" -  resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" -  integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== - -arr-union@^3.1.0: -  version "3.1.0" -  resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" -  integrity sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q== -  array-flatten@1.1.1:    version "1.1.1"    resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -1485,11 +1525,6 @@ array-union@^2.1.0:    resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"    integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -array-unique@^0.3.2: -  version "0.3.2" -  resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" -  integrity sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ== -  array.prototype.flatmap@^1.3.0:    version "1.3.0"    resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz#a7e8ed4225f4788a70cd910abcf0791e76a5534f" @@ -1523,21 +1558,11 @@ assert@^1.4.0:      object-assign "^4.1.1"      util "0.10.3" -assign-symbols@^1.0.0: -  version "1.0.0" -  resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" -  integrity sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw== -  assure-array@^1.0.0:    version "1.0.0"    resolved "https://registry.yarnpkg.com/assure-array/-/assure-array-1.0.0.tgz#4f4ad16a87659d6200a4fb7103462033d216ec1f"    integrity sha512-igvOvGYidAcJKr6YQIHzLivUpAdqUfi7MN0QfrEnFtifQvuw6D0W4oInrIVgTaefJ+QBVWAj8ZYuUGNnwq6Ydw== -ast-types@0.9.6: -  version "0.9.6" -  resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.6.tgz#102c9e9e9005d3e7e3829bf0c4fa24ee862ee9b9" -  integrity sha512-qEdtR2UH78yyHX/AUNfXmJTlM48XoFZKBdwi1nzkI1mJL21cmbu0cvjxjpkXJ5NENMq42H+hNs8VLJcqXLerBQ== -  astral-regex@^2.0.0:    version "2.0.0"    resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" @@ -1548,18 +1573,13 @@ async-limiter@~1.0.0:    resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"    integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== -atob@^2.1.2: -  version "2.1.2" -  resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" -  integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -  autoprefixer@^10.4.8: -  version "10.4.8" -  resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.8.tgz#92c7a0199e1cfb2ad5d9427bd585a3d75895b9e5" -  integrity sha512-75Jr6Q/XpTqEf6D2ltS5uMewJIx5irCU1oBYJrWjFenq/m12WRRrz6g15L1EIoYvPLXTbEry7rDOwrcYNj77xw== +  version "10.4.11" +  resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.11.tgz#835136aff1d9cd43640151e0d2dba00f8eac7c1c" +  integrity sha512-5lHp6DgRodxlBLSkzHOTcufWFflH1ewfy2hvFQyjrblBFlP/0Yh4O/Wrg4ow8WRlN3AAUFFLAQwX8hTptzqVHg==    dependencies:      browserslist "^4.21.3" -    caniuse-lite "^1.0.30001373" +    caniuse-lite "^1.0.30001399"      fraction.js "^4.2.0"      normalize-range "^0.1.2"      picocolors "^1.0.0" @@ -1577,29 +1597,29 @@ babel-plugin-dynamic-import-node@^2.3.3:    dependencies:      object.assign "^4.1.0" -babel-plugin-polyfill-corejs2@^0.3.2: -  version "0.3.2" -  resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.2.tgz#e4c31d4c89b56f3cf85b92558954c66b54bd972d" -  integrity sha512-LPnodUl3lS0/4wN3Rb+m+UK8s7lj2jcLRrjho4gLw+OJs+I4bvGXshINesY5xx/apM+biTnQ9reDI8yj+0M5+Q== +babel-plugin-polyfill-corejs2@^0.3.3: +  version "0.3.3" +  resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz#5d1bd3836d0a19e1b84bbf2d9640ccb6f951c122" +  integrity sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==    dependencies:      "@babel/compat-data" "^7.17.7" -    "@babel/helper-define-polyfill-provider" "^0.3.2" +    "@babel/helper-define-polyfill-provider" "^0.3.3"      semver "^6.1.1" -babel-plugin-polyfill-corejs3@^0.5.3: -  version "0.5.3" -  resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz#d7e09c9a899079d71a8b670c6181af56ec19c5c7" -  integrity sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw== +babel-plugin-polyfill-corejs3@^0.6.0: +  version "0.6.0" +  resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz#56ad88237137eade485a71b52f72dbed57c6230a" +  integrity sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==    dependencies: -    "@babel/helper-define-polyfill-provider" "^0.3.2" -    core-js-compat "^3.21.0" +    "@babel/helper-define-polyfill-provider" "^0.3.3" +    core-js-compat "^3.25.1" -babel-plugin-polyfill-regenerator@^0.4.0: -  version "0.4.0" -  resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.0.tgz#8f51809b6d5883e07e71548d75966ff7635527fe" -  integrity sha512-RW1cnryiADFeHmfLS+WW/G431p1PsW5qdRdz0SDRi7TKcUgc7Oh/uXkT7MZ/+tGsT1BkczEAmD5XjUyJ5SWDTw== +babel-plugin-polyfill-regenerator@^0.4.1: +  version "0.4.1" +  resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz#390f91c38d90473592ed43351e801a9d3e0fd747" +  integrity sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==    dependencies: -    "@babel/helper-define-polyfill-provider" "^0.3.2" +    "@babel/helper-define-polyfill-provider" "^0.3.3"  babelify@^10.0.0:    version "10.0.0" @@ -1611,29 +1631,11 @@ balanced-match@^1.0.0:    resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"    integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base62@0.1.1: -  version "0.1.1" -  resolved "https://registry.yarnpkg.com/base62/-/base62-0.1.1.tgz#7b4174c2f94449753b11c2651c083da841a7b084" -  integrity sha512-QtExujIOq/F672OkHmDi3CdkphOA1kSQ38gv03Ro3cplYQk831dq9GM3Q1oXAxpR5HNJjGjjjT2pHtBGAJu1jw== -  base64-js@^1.0.2, base64-js@^1.3.1:    version "1.5.1"    resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"    integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -base@^0.11.1: -  version "0.11.2" -  resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" -  integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== -  dependencies: -    cache-base "^1.0.1" -    class-utils "^0.3.5" -    component-emitter "^1.2.1" -    define-property "^1.0.0" -    isobject "^3.0.1" -    mixin-deep "^1.2.0" -    pascalcase "^0.1.1" -  big.js@^5.2.2:    version "5.2.2"    resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" @@ -1703,23 +1705,7 @@ brace-expansion@^1.1.7:      balanced-match "^1.0.0"      concat-map "0.0.1" -braces@^2.3.1: -  version "2.3.2" -  resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" -  integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== -  dependencies: -    arr-flatten "^1.1.0" -    array-unique "^0.3.2" -    extend-shallow "^2.0.1" -    fill-range "^4.0.0" -    isobject "^3.0.1" -    repeat-element "^1.1.2" -    snapdragon "^0.8.1" -    snapdragon-node "^2.0.1" -    split-string "^3.0.2" -    to-regex "^3.0.1" - -braces@~3.0.2: +braces@^3.0.2, braces@~3.0.2:    version "3.0.2"    resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"    integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -1822,7 +1808,7 @@ browserify-zlib@~0.2.0:    dependencies:      pako "~1.0.5" -browserify@^16.2.3, browserify@^16.5.0: +browserify@^16.5.0:    version "16.5.2"    resolved "https://registry.yarnpkg.com/browserify/-/browserify-16.5.2.tgz#d926835e9280fa5fd57f5bc301f2ef24a972ddfe"    integrity sha512-TkOR1cQGdmXU9zW4YukWzWVSJwrxmNdADFbqbE3HFgQWe5wqZmOawqZ7J/8MPCwk/W8yY7Y0h+7mOtcZxLP23g== @@ -1937,40 +1923,23 @@ browserlist@^1.0.1:    dependencies:      chalk "^2.4.1" -browserslist@^4.20.2, browserslist@^4.21.3: -  version "4.21.3" -  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.3.tgz#5df277694eb3c48bc5c4b05af3e8b7e09c5a6d1a" -  integrity sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ== +browserslist@^4.21.3: +  version "4.21.4" +  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" +  integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==    dependencies: -    caniuse-lite "^1.0.30001370" -    electron-to-chromium "^1.4.202" +    caniuse-lite "^1.0.30001400" +    electron-to-chromium "^1.4.251"      node-releases "^2.0.6" -    update-browserslist-db "^1.0.5" - -budo-express@^1.0.8: -  version "1.0.8" -  resolved "https://registry.yarnpkg.com/budo-express/-/budo-express-1.0.8.tgz#f1b325579453a1cd8ae510c25071bda89d594c08" -  integrity sha512-l7UXna1XB1jsH4dBPSv7AOWN9gekrUKDtwc+JrdiFhUNKsCPBWA+x62/rXqyzlcnthFgtfYhNnxtI9uh/FTpjQ== -  dependencies: -    assure-array "^1.0.0" -    bluebird "^3.7.2" -    browserify "^16.5.0" -    budo "^11.5.0" -    chalk "^3.0.0" -    default-value "^1.0.0" -    entities "^2.0.0" -    inject-lr-script "^2.2.0" -    is-stream "^2.0.0" -    stacked "^1.1.1" -    validatem "^0.2.0" +    update-browserslist-db "^1.0.9"  budo@^11.5.0: -  version "11.7.0" -  resolved "https://registry.yarnpkg.com/budo/-/budo-11.7.0.tgz#544a11e424972de9bdcf28102c0f0675f7176648" -  integrity sha512-nkulzTNOulJR7PBL+obojWvkSmblxlpSWz6dVsAG2QWyEJmCmtdIUYMpqIEzpFBvR9I/2sW2S0Q1xkDBIQ4pgA== +  version "11.8.4" +  resolved "https://registry.yarnpkg.com/budo/-/budo-11.8.4.tgz#75d732958e6b7caff7cdeeab65d98991f1325b65" +  integrity sha512-drUnbk6nAuzQ4xmyWjajvUb85ZhGduXpblY9guD776HmPqWoShlEE8XiYX145v7+ZoqznnShI3QHAObK9YSWnQ==    dependencies:      bole "^2.0.0" -    browserify "^16.2.3" +    browserify "^17.0.0"      chokidar "^3.5.2"      connect-pushstate "^1.1.0"      escape-html "^1.0.3" @@ -1979,7 +1948,7 @@ budo@^11.5.0:      get-ports "^1.0.2"      inject-lr-script "^2.1.0"      internal-ip "^3.0.1" -    micromatch "^3.1.10" +    micromatch "^4.0.5"      on-finished "^2.3.0"      on-headers "^1.0.1"      once "^1.3.2" @@ -1996,7 +1965,7 @@ budo@^11.5.0:      subarg "^1.0.0"      term-color "^1.0.1"      url-trim "^1.0.0" -    watchify-middleware "^1.8.2" +    watchify-middleware "^1.9.1"      ws "^6.2.2"      xtend "^4.0.0" @@ -2036,21 +2005,6 @@ bytes@3.1.2:    resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"    integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== -cache-base@^1.0.1: -  version "1.0.1" -  resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" -  integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== -  dependencies: -    collection-visit "^1.0.0" -    component-emitter "^1.2.1" -    get-value "^2.0.6" -    has-value "^1.0.0" -    isobject "^3.0.1" -    set-value "^2.0.0" -    to-object-path "^0.3.0" -    union-value "^1.0.0" -    unset-value "^1.0.0" -  cached-path-relative@^1.0.0, cached-path-relative@^1.0.2:    version "1.1.0"    resolved "https://registry.yarnpkg.com/cached-path-relative/-/cached-path-relative-1.1.0.tgz#865576dfef39c0d6a7defde794d078f5308e3ef3" @@ -2069,10 +2023,10 @@ callsites@^3.0.0:    resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"    integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -caniuse-lite@^1.0.30001370, caniuse-lite@^1.0.30001373: -  version "1.0.30001374" -  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001374.tgz#3dab138e3f5485ba2e74bd13eca7fe1037ce6f57" -  integrity sha512-mWvzatRx3w+j5wx/mpFN5v5twlPrabG8NqX2c6e45LCpymdoGqNvRkRutFUqpRTXKFQFNQJasvK0YT7suW6/Hw== +caniuse-lite@^1.0.30001399, caniuse-lite@^1.0.30001400: +  version "1.0.30001402" +  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001402.tgz#aa29e1f47f5055b0d0c07696a67b8b08023d14c8" +  integrity sha512-Mx4MlhXO5NwuvXGgVb+hg65HZ+bhUYsz8QtDGDo2QmaJS2GBX47Xfi2koL86lc8K+l+htXeTEB/Aeqvezoo6Ew==  chalk@^0.5.1:    version "0.5.1" @@ -2149,16 +2103,6 @@ cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:      inherits "^2.0.1"      safe-buffer "^5.0.1" -class-utils@^0.3.5: -  version "0.3.6" -  resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" -  integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== -  dependencies: -    arr-union "^3.1.0" -    define-property "^0.2.5" -    isobject "^3.0.0" -    static-extend "^0.1.1" -  clone-regexp@^2.1.0:    version "2.2.0"    resolved "https://registry.yarnpkg.com/clone-regexp/-/clone-regexp-2.2.0.tgz#7d65e00885cd8796405c35a737e7a86b7429e36f" @@ -2166,14 +2110,6 @@ clone-regexp@^2.1.0:    dependencies:      is-regexp "^2.0.0" -collection-visit@^1.0.0: -  version "1.0.0" -  resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" -  integrity sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw== -  dependencies: -    map-visit "^1.0.0" -    object-visit "^1.0.0" -  color-convert@^1.9.0:    version "1.9.3"    resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -2218,31 +2154,11 @@ combine-source-map@~0.6.1:      lodash.memoize "~3.0.3"      source-map "~0.4.2" -commander@^2.19.0, commander@^2.5.0: +commander@^2.19.0:    version "2.20.3"    resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"    integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -commoner@^0.10.0: -  version "0.10.8" -  resolved "https://registry.yarnpkg.com/commoner/-/commoner-0.10.8.tgz#34fc3672cd24393e8bb47e70caa0293811f4f2c5" -  integrity sha512-3/qHkNMM6o/KGXHITA14y78PcfmXh4+AOCJpSoF73h4VY1JpdGv3CHMS5+JW6SwLhfJt4RhNmLAa7+RRX/62EQ== -  dependencies: -    commander "^2.5.0" -    detective "^4.3.1" -    glob "^5.0.15" -    graceful-fs "^4.1.2" -    iconv-lite "^0.4.5" -    mkdirp "^0.5.0" -    private "^0.1.6" -    q "^1.1.2" -    recast "^0.11.17" - -component-emitter@^1.2.1: -  version "1.3.0" -  resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" -  integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== -  concat-map@0.0.1:    version "0.0.1"    resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -2307,18 +2223,12 @@ cookie@0.5.0:    resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"    integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== -copy-descriptor@^0.1.0: -  version "0.1.1" -  resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" -  integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== - -core-js-compat@^3.21.0, core-js-compat@^3.22.1: -  version "3.24.1" -  resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.24.1.tgz#d1af84a17e18dfdd401ee39da9996f9a7ba887de" -  integrity sha512-XhdNAGeRnTpp8xbD+sR/HFDK9CbeeeqXT6TuofXh3urqEevzkWmLRgrVoykodsw8okqo2pu1BOmuCKrHx63zdw== +core-js-compat@^3.25.1: +  version "3.25.1" +  resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.25.1.tgz#6f13a90de52f89bbe6267e5620a412c7f7ff7e42" +  integrity sha512-pOHS7O0i8Qt4zlPW/eIFjwp+NrTPx+wTL0ctgI2fHn31sZOq89rDsmtc/A2vAX7r6shl+bmVI+678He46jgBlw==    dependencies:      browserslist "^4.21.3" -    semver "7.0.0"  "core-util-is@>=1.0.1 <1.1.0-0", core-util-is@~1.0.0:    version "1.0.3" @@ -2419,6 +2329,11 @@ cssesc@^3.0.0:    resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"    integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== +csstype@^3.0.2: +  version "3.1.1" +  resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9" +  integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw== +  d@1, d@^1.0.1:    version "1.0.1"    resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" @@ -2442,7 +2357,7 @@ debounce@^1.0.0:    resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5"    integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== -debug@2.6.9, debug@^2.2.0, debug@^2.3.3: +debug@2.6.9, debug@^2.2.0:    version "2.6.9"    resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"    integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -2456,11 +2371,6 @@ debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:    dependencies:      ms "2.1.2" -decode-uri-component@^0.2.0: -  version "0.2.0" -  resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" -  integrity sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og== -  deep-is@^0.1.3, deep-is@~0.1.3:    version "0.1.4"    resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -2489,28 +2399,6 @@ define-properties@^1.1.3, define-properties@^1.1.4:      has-property-descriptors "^1.0.0"      object-keys "^1.1.1" -define-property@^0.2.5: -  version "0.2.5" -  resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" -  integrity sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA== -  dependencies: -    is-descriptor "^0.1.0" - -define-property@^1.0.0: -  version "1.0.0" -  resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" -  integrity sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA== -  dependencies: -    is-descriptor "^1.0.0" - -define-property@^2.0.2: -  version "2.0.2" -  resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" -  integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== -  dependencies: -    is-descriptor "^1.0.2" -    isobject "^3.0.1" -  defined@0.0.0:    version "0.0.0"    resolved "https://registry.yarnpkg.com/defined/-/defined-0.0.0.tgz#f35eea7d705e933baf13b2f03b3f83d921403b3e" @@ -2558,14 +2446,6 @@ destroy@1.2.0:    resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"    integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== -detective@^4.3.1: -  version "4.7.1" -  resolved "https://registry.yarnpkg.com/detective/-/detective-4.7.1.tgz#0eca7314338442febb6d65da54c10bb1c82b246e" -  integrity sha512-H6PmeeUcZloWtdt4DAkFyzFL94arpHr3NOwwmVILFiy+9Qd4JTxxXrzfyGk/lmct2qVGBwTSwSXagqu2BxmWig== -  dependencies: -    acorn "^5.2.1" -    defined "^1.0.0" -  detective@^5.2.0:    version "5.2.1"    resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.1.tgz#6af01eeda11015acb0e73f933242b70f24f91034" @@ -2603,6 +2483,11 @@ domain-browser@^1.2.0:    resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda"    integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== +dotty@^0.1.2: +  version "0.1.2" +  resolved "https://registry.yarnpkg.com/dotty/-/dotty-0.1.2.tgz#512d44cc4111a724931226259297f235e8484f6f" +  integrity sha512-V0EWmKeH3DEhMwAZ+8ZB2Ao4OK6p++Z0hsDtZq3N0+0ZMVqkzrcEGROvOnZpLnvBg5PTNG23JEDLAm64gPaotQ== +  duplexer2@^0.1.2, duplexer2@~0.1.0, duplexer2@~0.1.2, duplexer2@~0.1.4:    version "0.1.4"    resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" @@ -2620,10 +2505,10 @@ ee-first@1.1.1:    resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"    integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== -electron-to-chromium@^1.4.202: -  version "1.4.211" -  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.211.tgz#afaa8b58313807501312d598d99b953568d60f91" -  integrity sha512-BZSbMpyFQU0KBJ1JG26XGeFI3i4op+qOYGxftmZXFZoHkhLgsSv4DHDJfl8ogII3hIuzGt51PaZ195OVu0yJ9A== +electron-to-chromium@^1.4.251: +  version "1.4.253" +  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.253.tgz#3402fd2159530fc6d94237f1b9535fa7bebaf399" +  integrity sha512-1pezJ2E1UyBTGbA7fUlHdPSXQw1k+82VhTFLG5G0AUqLGvsZqFzleOblceqegZzxYX4kC7hGEEdzIQI9RZ1Cuw==  elliptic@^6.5.3:    version "6.5.4" @@ -2666,15 +2551,15 @@ entities@^2.0.0:    integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==  es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5, es-abstract@^1.20.0: -  version "1.20.1" -  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814" -  integrity sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA== +  version "1.20.2" +  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.2.tgz#8495a07bc56d342a3b8ea3ab01bd986700c2ccb3" +  integrity sha512-XxXQuVNrySBNlEkTYJoDNFe5+s2yIOpzq80sUHEdPdQr0S5nTLz4ZPPPswNIpKseDDUS5yghX1gfLIHQZ1iNuQ==    dependencies:      call-bind "^1.0.2"      es-to-primitive "^1.2.1"      function-bind "^1.1.1"      function.prototype.name "^1.1.5" -    get-intrinsic "^1.1.1" +    get-intrinsic "^1.1.2"      get-symbol-description "^1.0.0"      has "^1.0.3"      has-property-descriptors "^1.0.0" @@ -2686,9 +2571,9 @@ es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19      is-shared-array-buffer "^1.0.2"      is-string "^1.0.7"      is-weakref "^1.0.2" -    object-inspect "^1.12.0" +    object-inspect "^1.12.2"      object-keys "^1.1.1" -    object.assign "^4.1.2" +    object.assign "^4.1.4"      regexp.prototype.flags "^1.4.3"      string.prototype.trimend "^1.0.5"      string.prototype.trimstart "^1.0.5" @@ -2710,7 +2595,7 @@ es-to-primitive@^1.2.1:      is-date-object "^1.0.1"      is-symbol "^1.0.2" -es5-ext@^0.10.35, es5-ext@^0.10.50, es5-ext@~0.10.14: +es5-ext@^0.10.35, es5-ext@^0.10.50, es5-ext@^0.10.62, es5-ext@~0.10.14:    version "0.10.62"    resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5"    integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA== @@ -2719,7 +2604,7 @@ es5-ext@^0.10.35, es5-ext@^0.10.50, es5-ext@~0.10.14:      es6-symbol "^3.1.3"      next-tick "^1.1.0" -es6-iterator@^2.0.3, es6-iterator@~2.0.1: +es6-iterator@^2.0.3, es6-iterator@~2.0.1, es6-iterator@~2.0.3:    version "2.0.3"    resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7"    integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== @@ -2751,23 +2636,16 @@ es6-promisify@^6.0.0:    integrity sha512-HBL8I3mIki5C1Cc9QjKUenHtnG0A5/xA8Q/AllRcfiwl2CZFXGK7ddBiCoRwAix4i2KxcQfjtIVcrVbB3vbmwg==  es6-set@^0.1.5, es6-set@~0.1.5: -  version "0.1.5" -  resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1" -  integrity sha512-7S8YXIcUfPMOr3rqJBVMePAbRsD1nWeSMQ86K/lDI76S3WKXz+KWILvTIPbTroubOkZTGh+b+7/xIIphZXNYbA== -  dependencies: -    d "1" -    es5-ext "~0.10.14" -    es6-iterator "~2.0.1" -    es6-symbol "3.1.1" -    event-emitter "~0.3.5" - -es6-symbol@3.1.1: -  version "3.1.1" -  resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" -  integrity sha512-exfuQY8UGtn/N+gL1iKkH8fpNd5sJ760nJq6mmZAHldfxMD5kX07lbQuYlspoXsuknXNv9Fb7y2GsPOnQIbxHg== +  version "0.1.6" +  resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.6.tgz#5669e3b2aa01d61a50ba79964f733673574983b8" +  integrity sha512-TE3LgGLDIBX332jq3ypv6bcOpkLO0AslAQo7p2VqX/1N46YNsvIWgvjojjSEnWEGWMhr1qUbYeTSir5J6mFHOw==    dependencies: -    d "1" -    es5-ext "~0.10.14" +    d "^1.0.1" +    es5-ext "^0.10.62" +    es6-iterator "~2.0.3" +    es6-symbol "^3.1.3" +    event-emitter "^0.3.5" +    type "^2.7.2"  es6-symbol@^3.1.1, es6-symbol@^3.1.3, es6-symbol@~3.1.1:    version "3.1.3" @@ -2820,9 +2698,9 @@ eslint-plugin-react-hooks@^4.2.0:    integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==  eslint-plugin-react@^7.14.3, eslint-plugin-react@^7.24.0: -  version "7.30.1" -  resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.30.1.tgz#2be4ab23ce09b5949c6631413ba64b2810fd3e22" -  integrity sha512-NbEvI9jtqO46yJA3wcRF9Mo0lF9T/jhdHqhCHXiXtD+Zcb98812wvokjWpU7Q4QH5edo6dmqrukxVvWWXHlsUg== +  version "7.31.8" +  resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.31.8.tgz#3a4f80c10be1bcbc8197be9e8b641b2a3ef219bf" +  integrity sha512-5lBTZmgQmARLLSYiwI71tiGVTLUuqXantZM6vlSY39OaDSV0M7+32K5DnLkmFrwTe+Ksz0ffuLUC91RUviVZfw==    dependencies:      array-includes "^3.1.5"      array.prototype.flatmap "^1.3.0" @@ -2919,21 +2797,11 @@ espree@^7.3.0, espree@^7.3.1:      acorn-jsx "^5.3.1"      eslint-visitor-keys "^1.3.0" -esprima-fb@13001.1001.0-dev-harmony-fb: -  version "13001.1001.0-dev-harmony-fb" -  resolved "https://registry.yarnpkg.com/esprima-fb/-/esprima-fb-13001.1001.0-dev-harmony-fb.tgz#633acdb40d9bd4db8a1c1d68c06a942959fad2b0" -  integrity sha512-u0PLCs9J36198vK7lFdvzfOiMT2v2K9/9d+J2M4d1ZEfTsXzvrzRHh95D+/sIziSabl4b6QKJOTn8+VaWc/B4A== -  esprima@^4.0.0, esprima@^4.0.1:    version "4.0.1"    resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"    integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esprima@~3.1.0: -  version "3.1.3" -  resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" -  integrity sha512-AWwVMNxwhN8+NIPQzAQZCm7RkLC4RbM3B1OobMuyp3i+w73X57KCKaVIxaRZb+DYCojq7rspo+fmuQfAboyhFg== -  esquery@^1.4.0:    version "1.4.0"    resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" @@ -2973,7 +2841,7 @@ etag@~1.8.1:    resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"    integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== -event-emitter@~0.3.5: +event-emitter@^0.3.5, event-emitter@~0.3.5:    version "0.3.5"    resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39"    integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA== @@ -3024,19 +2892,6 @@ execall@^2.0.0:    dependencies:      clone-regexp "^2.1.0" -expand-brackets@^2.1.4: -  version "2.1.4" -  resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" -  integrity sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA== -  dependencies: -    debug "^2.3.3" -    define-property "^0.2.5" -    extend-shallow "^2.0.1" -    posix-character-classes "^0.1.0" -    regex-not "^1.0.0" -    snapdragon "^0.8.1" -    to-regex "^3.0.1" -  express@^4.18.1:    version "4.18.1"    resolved "https://registry.yarnpkg.com/express/-/express-4.18.1.tgz#7797de8b9c72c857b9cd0e14a5eea80666267caf" @@ -3075,40 +2930,11 @@ express@^4.18.1:      vary "~1.1.2"  ext@^1.1.2: -  version "1.6.0" -  resolved "https://registry.yarnpkg.com/ext/-/ext-1.6.0.tgz#3871d50641e874cc172e2b53f919842d19db4c52" -  integrity sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg== -  dependencies: -    type "^2.5.0" - -extend-shallow@^2.0.1: -  version "2.0.1" -  resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" -  integrity sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug== -  dependencies: -    is-extendable "^0.1.0" - -extend-shallow@^3.0.0, extend-shallow@^3.0.2: -  version "3.0.2" -  resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" -  integrity sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q== +  version "1.7.0" +  resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" +  integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==    dependencies: -    assign-symbols "^1.0.0" -    is-extendable "^1.0.1" - -extglob@^2.0.4: -  version "2.0.4" -  resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" -  integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== -  dependencies: -    array-unique "^0.3.2" -    define-property "^1.0.0" -    expand-brackets "^2.1.4" -    extend-shallow "^2.0.1" -    fragment-cache "^0.2.1" -    regex-not "^1.0.0" -    snapdragon "^0.8.1" -    to-regex "^3.0.1" +    type "^2.7.2"  factor-bundle@^2.5.0:    version "2.5.0" @@ -3157,16 +2983,6 @@ file-entry-cache@^6.0.1:    dependencies:      flat-cache "^3.0.4" -fill-range@^4.0.0: -  version "4.0.0" -  resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" -  integrity sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ== -  dependencies: -    extend-shallow "^2.0.1" -    is-number "^3.0.0" -    repeat-string "^1.6.1" -    to-regex-range "^2.1.0" -  fill-range@^7.0.1:    version "7.0.1"    resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -3201,9 +3017,9 @@ flat-cache@^3.0.4:      rimraf "^3.0.2"  flatted@^3.1.0: -  version "3.2.6" -  resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.6.tgz#022e9218c637f9f3fc9c35ab9c9193f05add60b2" -  integrity sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ== +  version "3.2.7" +  resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" +  integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==  flatten@^1.0.2, flatten@^1.0.3:    version "1.0.3" @@ -3217,11 +3033,6 @@ for-each@^0.3.3:    dependencies:      is-callable "^1.1.3" -for-in@^1.0.2: -  version "1.0.2" -  resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" -  integrity sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ== -  forwarded@0.2.0:    version "0.2.0"    resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -3232,13 +3043,6 @@ fraction.js@^4.2.0:    resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950"    integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA== -fragment-cache@^0.2.1: -  version "0.2.1" -  resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" -  integrity sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA== -  dependencies: -    map-cache "^0.2.2" -  fresh@0.5.2:    version "0.5.2"    resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" @@ -3327,10 +3131,10 @@ get-assigned-identifiers@^1.1.0, get-assigned-identifiers@^1.2.0:    resolved "https://registry.yarnpkg.com/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz#6dbf411de648cbaf8d9169ebb0d2d576191e2ff1"    integrity sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: -  version "1.1.2" -  resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598" -  integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA== +get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@^1.1.2: +  version "1.1.3" +  resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385" +  integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==    dependencies:      function-bind "^1.1.1"      has "^1.0.3" @@ -3356,11 +3160,6 @@ get-symbol-description@^1.0.0:      call-bind "^1.0.2"      get-intrinsic "^1.1.1" -get-value@^2.0.3, get-value@^2.0.6: -  version "2.0.6" -  resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" -  integrity sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA== -  glob-parent@^5.1.2, glob-parent@~5.1.2:    version "5.1.2"    resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -3368,17 +3167,6 @@ glob-parent@^5.1.2, glob-parent@~5.1.2:    dependencies:      is-glob "^4.0.1" -glob@^5.0.15: -  version "5.0.15" -  resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" -  integrity sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA== -  dependencies: -    inflight "^1.0.4" -    inherits "2" -    minimatch "2 || 3" -    once "^1.3.0" -    path-is-absolute "^1.0.0" -  glob@^7.1.0, glob@^7.1.3:    version "7.2.3"    resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" @@ -3403,11 +3191,6 @@ globals@^13.6.0, globals@^13.9.0:    dependencies:      type-fest "^0.20.2" -graceful-fs@^4.1.2: -  version "4.2.10" -  resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" -  integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== -  has-ansi@^0.1.0:    version "0.1.0"    resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-0.1.0.tgz#84f265aae8c0e6a88a12d7022894b7568894c62e" @@ -3461,37 +3244,6 @@ has-tostringtag@^1.0.0:    dependencies:      has-symbols "^1.0.2" -has-value@^0.3.1: -  version "0.3.1" -  resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" -  integrity sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q== -  dependencies: -    get-value "^2.0.3" -    has-values "^0.1.4" -    isobject "^2.0.0" - -has-value@^1.0.0: -  version "1.0.0" -  resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" -  integrity sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw== -  dependencies: -    get-value "^2.0.6" -    has-values "^1.0.0" -    isobject "^3.0.0" - -has-values@^0.1.4: -  version "0.1.4" -  resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" -  integrity sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ== - -has-values@^1.0.0: -  version "1.0.0" -  resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" -  integrity sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ== -  dependencies: -    is-number "^3.0.0" -    kind-of "^4.0.0" -  has@^1.0.0, has@^1.0.1, has@^1.0.3:    version "1.0.3"    resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" @@ -3525,6 +3277,13 @@ hmac-drbg@^1.0.1:      minimalistic-assert "^1.0.0"      minimalistic-crypto-utils "^1.0.1" +hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: +  version "3.3.2" +  resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" +  integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== +  dependencies: +    react-is "^16.7.0" +  htmlescape@^1.1.0:    version "1.1.1"    resolved "https://registry.yarnpkg.com/htmlescape/-/htmlescape-1.1.1.tgz#3a03edc2214bca3b66424a3e7959349509cb0351" @@ -3546,7 +3305,7 @@ https-browserify@^1.0.0:    resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"    integrity sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg== -iconv-lite@0.4.24, iconv-lite@^0.4.5: +iconv-lite@0.4.24:    version "0.4.24"    resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"    integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -3598,6 +3357,11 @@ ignore@^4.0.6:    resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"    integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== +immer@^9.0.7: +  version "9.0.15" +  resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.15.tgz#0b9169e5b1d22137aba7d43f8a81a495dd1b62dc" +  integrity sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ== +  import-fresh@^3.0.0, import-fresh@^3.2.1:    version "3.3.0"    resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -3723,20 +3487,6 @@ ipaddr.js@1.9.1, ipaddr.js@^1.5.2:    resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"    integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== -is-accessor-descriptor@^0.1.6: -  version "0.1.6" -  resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" -  integrity sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A== -  dependencies: -    kind-of "^3.0.2" - -is-accessor-descriptor@^1.0.0: -  version "1.0.0" -  resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" -  integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== -  dependencies: -    kind-of "^6.0.0" -  is-arguments@^1.0.4:    version "1.1.1"    resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" @@ -3767,15 +3517,15 @@ is-boolean-object@^1.0.1, is-boolean-object@^1.1.0:      call-bind "^1.0.2"      has-tostringtag "^1.0.0" -is-buffer@^1.1.0, is-buffer@^1.1.5, is-buffer@~1.1.6: +is-buffer@^1.1.0, is-buffer@~1.1.6:    version "1.1.6"    resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"    integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==  is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.1.5, is-callable@^1.2.4: -  version "1.2.4" -  resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" -  integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== +  version "1.2.6" +  resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.6.tgz#fd6170b0b8c7e2cc73de342ef8284a2202023c44" +  integrity sha512-krO72EO2NptOGAX2KYyqbP9vYMlNAXdB53rq6f8LXY6RY7JdSR/3BD6wLUlPHSAesmY9vstNrjvqGaCiRK/91Q==  is-core-module@^2.9.0:    version "2.10.0" @@ -3784,20 +3534,6 @@ is-core-module@^2.9.0:    dependencies:      has "^1.0.3" -is-data-descriptor@^0.1.4: -  version "0.1.4" -  resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" -  integrity sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg== -  dependencies: -    kind-of "^3.0.2" - -is-data-descriptor@^1.0.0: -  version "1.0.0" -  resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" -  integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== -  dependencies: -    kind-of "^6.0.0" -  is-date-object@^1.0.1:    version "1.0.5"    resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" @@ -3805,36 +3541,6 @@ is-date-object@^1.0.1:    dependencies:      has-tostringtag "^1.0.0" -is-descriptor@^0.1.0: -  version "0.1.6" -  resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" -  integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== -  dependencies: -    is-accessor-descriptor "^0.1.6" -    is-data-descriptor "^0.1.4" -    kind-of "^5.0.0" - -is-descriptor@^1.0.0, is-descriptor@^1.0.2: -  version "1.0.2" -  resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" -  integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== -  dependencies: -    is-accessor-descriptor "^1.0.0" -    is-data-descriptor "^1.0.0" -    kind-of "^6.0.2" - -is-extendable@^0.1.0, is-extendable@^0.1.1: -  version "0.1.1" -  resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" -  integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== - -is-extendable@^1.0.1: -  version "1.0.1" -  resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" -  integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== -  dependencies: -    is-plain-object "^2.0.4" -  is-extglob@^2.1.1:    version "2.1.1"    resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -3876,13 +3582,6 @@ is-number-object@^1.0.4:    dependencies:      has-tostringtag "^1.0.0" -is-number@^3.0.0: -  version "3.0.0" -  resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" -  integrity sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg== -  dependencies: -    kind-of "^3.0.2" -  is-number@^7.0.0:    version "7.0.0"    resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -3893,12 +3592,10 @@ is-plain-obj@^2.0.0, is-plain-obj@^2.1.0:    resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287"    integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== -is-plain-object@^2.0.3, is-plain-object@^2.0.4: -  version "2.0.4" -  resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" -  integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== -  dependencies: -    isobject "^3.0.1" +is-plain-object@^5.0.0: +  version "5.0.0" +  resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" +  integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==  is-regex@^1.1.4:    version "1.1.4" @@ -3955,6 +3652,13 @@ is-typed-array@^1.1.3, is-typed-array@^1.1.9:      for-each "^0.3.3"      has-tostringtag "^1.0.0" +is-valid-domain@^0.1.6: +  version "0.1.6" +  resolved "https://registry.yarnpkg.com/is-valid-domain/-/is-valid-domain-0.1.6.tgz#3c85469d2938f170c8f82ce6e52df8ad9fca8105" +  integrity sha512-ZKtq737eFkZr71At8NxOFcP9O1K89gW3DkdrGMpp1upr/ueWjj+Weh4l9AI4rN0Gt8W2M1w7jrG2b/Yv83Ljpg== +  dependencies: +    punycode "^2.1.1" +  is-weakref@^1.0.2:    version "1.0.2"    resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" @@ -3962,17 +3666,12 @@ is-weakref@^1.0.2:    dependencies:      call-bind "^1.0.2" -is-windows@^1.0.2: -  version "1.0.2" -  resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" -  integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== -  isarray@0.0.1, isarray@~0.0.1:    version "0.0.1"    resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"    integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== -isarray@1.0.0, isarray@~1.0.0: +isarray@~1.0.0:    version "1.0.0"    resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"    integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== @@ -3982,18 +3681,6 @@ isexe@^2.0.0:    resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"    integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== -isobject@^2.0.0: -  version "2.1.0" -  resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" -  integrity sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA== -  dependencies: -    isarray "1.0.0" - -isobject@^3.0.0, isobject@^3.0.1: -  version "3.0.1" -  resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" -  integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== -  js-base64@^2.1.9:    version "2.6.4"    resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.6.4.tgz#f4e686c5de1ea1f867dbcad3d46d969428df98c4" @@ -4081,46 +3768,13 @@ jsonparse@^1.2.0:    resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280"    integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== -jstransform@^10.1.0: -  version "10.1.0" -  resolved "https://registry.yarnpkg.com/jstransform/-/jstransform-10.1.0.tgz#b4c49bf63f162c108b0348399a8737c713b0a83a" -  integrity sha512-hzsCrPlH8ASlARV/sjsjbnvg0AXz9DxMHry44wXF3GTvletHT8UhsmqUzSGaImlney40E1gw4D6izUzifD15IQ== -  dependencies: -    base62 "0.1.1" -    esprima-fb "13001.1001.0-dev-harmony-fb" -    source-map "0.1.31" -  "jsx-ast-utils@^2.4.1 || ^3.0.0": -  version "3.3.2" -  resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.2.tgz#afe5efe4332cd3515c065072bd4d6b0aa22152bd" -  integrity sha512-4ZCADZHRkno244xlNnn4AOG6sRQ7iBZ5BbgZ4vW4y5IZw7cVUD1PPeblm1xx/nfmMxPdt/LHsXZW8z/j58+l9Q== +  version "3.3.3" +  resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz#76b3e6e6cece5c69d49a5792c3d01bd1a0cdc7ea" +  integrity sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==    dependencies:      array-includes "^3.1.5" -    object.assign "^4.1.2" - -kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: -  version "3.2.2" -  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" -  integrity sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ== -  dependencies: -    is-buffer "^1.1.5" - -kind-of@^4.0.0: -  version "4.0.0" -  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" -  integrity sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw== -  dependencies: -    is-buffer "^1.1.5" - -kind-of@^5.0.0: -  version "5.1.0" -  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" -  integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== - -kind-of@^6.0.0, kind-of@^6.0.2: -  version "6.0.3" -  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" -  integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== +    object.assign "^4.1.3"  labeled-stream-splicer@^1.0.0:    version "1.0.2" @@ -4205,11 +3859,6 @@ magic-string@0.25.1:    dependencies:      sourcemap-codec "^1.4.1" -map-cache@^0.2.2: -  version "0.2.2" -  resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" -  integrity sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg== -  map-limit@0.0.1:    version "0.0.1"    resolved "https://registry.yarnpkg.com/map-limit/-/map-limit-0.0.1.tgz#eb7961031c0f0e8d001bf2d56fab685d58822f38" @@ -4222,13 +3871,6 @@ map-obj@^4.1.0:    resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a"    integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== -map-visit@^1.0.0: -  version "1.0.0" -  resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" -  integrity sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w== -  dependencies: -    object-visit "^1.0.0" -  md5.js@^1.3.4:    version "1.3.5"    resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" @@ -4269,24 +3911,13 @@ methods@~1.1.2:    resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"    integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== -micromatch@^3.1.10: -  version "3.1.10" -  resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" -  integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== -  dependencies: -    arr-diff "^4.0.0" -    array-unique "^0.3.2" -    braces "^2.3.1" -    define-property "^2.0.2" -    extend-shallow "^3.0.2" -    extglob "^2.0.4" -    fragment-cache "^0.2.1" -    kind-of "^6.0.2" -    nanomatch "^1.2.9" -    object.pick "^1.3.0" -    regex-not "^1.0.0" -    snapdragon "^0.8.1" -    to-regex "^3.0.2" +micromatch@^4.0.5: +  version "4.0.5" +  resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" +  integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== +  dependencies: +    braces "^3.0.2" +    picomatch "^2.3.1"  miller-rabin@^4.0.0:    version "4.0.1" @@ -4323,7 +3954,7 @@ minimalistic-crypto-utils@^1.0.1:    resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"    integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== -"minimatch@2 || 3", minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: +minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2:    version "3.1.2"    resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"    integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -4345,26 +3976,11 @@ minimist@~0.2.0:    resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.2.1.tgz#827ba4e7593464e7c221e8c5bed930904ee2c455"    integrity sha512-GY8fANSrTMfBVfInqJAY41QkOM+upUTytK1jZ0c8+3HdHrJxBJ3rF5i9moClXTE8uUSnUo8cAsCoxDXvSY4DHg== -mixin-deep@^1.2.0: -  version "1.3.2" -  resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" -  integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== -  dependencies: -    for-in "^1.0.2" -    is-extendable "^1.0.1" -  mkdirp-classic@^0.5.2:    version "0.5.3"    resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"    integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== -mkdirp@^0.5.0: -  version "0.5.6" -  resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" -  integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== -  dependencies: -    minimist "^1.2.6" -  modern-normalize@^1.1.0:    version "1.1.0"    resolved "https://registry.yarnpkg.com/modern-normalize/-/modern-normalize-1.1.0.tgz#da8e80140d9221426bd4f725c6e11283d34f90b7" @@ -4411,23 +4027,6 @@ nanoid@^3.3.4:    resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"    integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== -nanomatch@^1.2.9: -  version "1.2.13" -  resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" -  integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== -  dependencies: -    arr-diff "^4.0.0" -    array-unique "^0.3.2" -    define-property "^2.0.2" -    extend-shallow "^3.0.2" -    fragment-cache "^0.2.1" -    is-windows "^1.0.2" -    kind-of "^6.0.2" -    object.pick "^1.3.0" -    regex-not "^1.0.0" -    snapdragon "^0.8.1" -    to-regex "^3.0.1" -  natural-compare@^1.4.0:    version "1.4.0"    resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -4480,16 +4079,7 @@ object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:    resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"    integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-copy@^0.1.0: -  version "0.1.0" -  resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" -  integrity sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ== -  dependencies: -    copy-descriptor "^0.1.0" -    define-property "^0.2.5" -    kind-of "^3.0.3" - -object-inspect@^1.12.0, object-inspect@^1.6.0, object-inspect@^1.9.0: +object-inspect@^1.12.2, object-inspect@^1.6.0, object-inspect@^1.9.0:    version "1.12.2"    resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea"    integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== @@ -4499,17 +4089,10 @@ object-keys@^1.1.1:    resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"    integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object-visit@^1.0.0: -  version "1.0.1" -  resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" -  integrity sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA== -  dependencies: -    isobject "^3.0.0" - -object.assign@^4.1.0, object.assign@^4.1.2: -  version "4.1.3" -  resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.3.tgz#d36b7700ddf0019abb6b1df1bb13f6445f79051f" -  integrity sha512-ZFJnX3zltyjcYJL0RoCJuzb+11zWGyaDbjgxZbdV7rFEcHQuYxrZqhow67aA7xpes6LhojyFDaBKAFfogQrikA== +object.assign@^4.1.0, object.assign@^4.1.3, object.assign@^4.1.4: +  version "4.1.4" +  resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" +  integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==    dependencies:      call-bind "^1.0.2"      define-properties "^1.1.4" @@ -4542,13 +4125,6 @@ object.hasown@^1.1.1:      define-properties "^1.1.4"      es-abstract "^1.19.5" -object.pick@^1.3.0: -  version "1.3.0" -  resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" -  integrity sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ== -  dependencies: -    isobject "^3.0.1" -  object.values@^1.1.5:    version "1.1.5"    resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" @@ -4691,11 +4267,6 @@ parseurl@~1.3.3:    resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"    integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== -pascalcase@^0.1.1: -  version "0.1.1" -  resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" -  integrity sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw== -  path-browserify@^1.0.0:    version "1.0.1"    resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" @@ -4763,9 +4334,9 @@ photoswipe-dynamic-caption-plugin@^1.2.4:    integrity sha512-7gPO8BHnOGZ0nXVhziltDqe7cAhDmaolGaU6LC3vggi4dcTHJBsGnxuqpk2EXw6vJ/JOeS0MqyGGUXupYbZk5w==  photoswipe@^5.3.0: -  version "5.3.0" -  resolved "https://registry.yarnpkg.com/photoswipe/-/photoswipe-5.3.0.tgz#fe118b147dddaf58ccc17c9403c7d7148805f8d2" -  integrity sha512-vZMwziQorjiagzX7EvWimVT0YHO0DWNtR9UT6cv3yW1FA199LgsTpj4ziB2oJ/X/197gKmi56Oux5PudWUAmuw== +  version "5.3.2" +  resolved "https://registry.yarnpkg.com/photoswipe/-/photoswipe-5.3.2.tgz#814d26197ba59076828ddefd41b7f9ed5eb355a8" +  integrity sha512-QJrf0kGa3tYX3sUascZymkT+ZIkgw8YNcwL+hGqoLTyphcn9vSTEab7tmCnA1tthgVzWQRgPjX9psuk7yFrTcA==  picocolors@^0.2.1:    version "0.2.1" @@ -4777,7 +4348,7 @@ picocolors@^1.0.0:    resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"    integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.0.4, picomatch@^2.2.1: +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:    version "2.3.1"    resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"    integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -4792,11 +4363,6 @@ plur@^1.0.0:    resolved "https://registry.yarnpkg.com/plur/-/plur-1.0.0.tgz#db85c6814f5e5e5a3b49efc28d604fec62975156"    integrity sha512-qSnKBSZeDY8ApxwhfVIwKwF36KVJqb1/9nzYYq3j3vdwocULCXT8f8fQGkiw1Nk9BGfxiDagEe/pwakA+bOBqw== -posix-character-classes@^0.1.0: -  version "0.1.1" -  resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" -  integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== -  postcss-color-mod-function@^3.0.3:    version "3.0.3"    resolved "https://registry.yarnpkg.com/postcss-color-mod-function/-/postcss-color-mod-function-3.0.3.tgz#816ba145ac11cc3cb6baa905a75a49f903e4d31d" @@ -4856,9 +4422,9 @@ postcss-nested@^5.0.6:      postcss-selector-parser "^6.0.6"  postcss-scss@^4.0.4: -  version "4.0.4" -  resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-4.0.4.tgz#aa8f60e19ee18259bc193db9e4b96edfce3f3b1f" -  integrity sha512-aBBbVyzA8b3hUL0MGrpydxxXKXFZc5Eqva0Q3V9qsBOLEMsjb6w49WfpsoWzpEgcqJGW4t7Rio8WXVU9Gd8vWg== +  version "4.0.5" +  resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-4.0.5.tgz#8ee33c1dda8d9d4753b565ec79014803dc6edabf" +  integrity sha512-F7xpB6TrXyqUh3GKdyB4Gkp3QL3DDW1+uI+gxx/oJnUt/qXI4trj5OGlp9rOKdoABGULuqtqeG+3HEVQk4DjmA==  postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.6:    version "6.0.10" @@ -4926,15 +4492,15 @@ prelude-ls@~1.1.2:    resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"    integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== -prettier-bytes@^1.0.3: +prettier-bytes@^1.0.3, prettier-bytes@^1.0.4:    version "1.0.4"    resolved "https://registry.yarnpkg.com/prettier-bytes/-/prettier-bytes-1.0.4.tgz#994b02aa46f699c50b6257b5faaa7fe2557e62d6"    integrity sha512-dLbWOa4xBn+qeWeIF60qRoB6Pk2jX5P3DIVgOQyMyvBpu931Q+8dXz8X0snJiFkQdohDDLnZQECjzsAj75hgZQ== -pretty-bytes@^5.6.0: -  version "5.6.0" -  resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" -  integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== +pretty-bytes@4: +  version "4.0.2" +  resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-4.0.2.tgz#b2bf82e7350d65c6c33aa95aaa5a4f6327f61cd9" +  integrity sha512-yJAF+AjbHKlxQ8eezMd/34Mnj/YTQ3i6kLzvVsH4l/BfIFtp444n0wVbnsn66JimZ9uBofv815aRp1zCppxlWw==  pretty-ms@^2.1.0:    version "2.1.0" @@ -4945,11 +4511,6 @@ pretty-ms@^2.1.0:      parse-ms "^1.0.0"      plur "^1.0.0" -private@^0.1.6, private@~0.1.5: -  version "0.1.8" -  resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" -  integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== -  process-nextick-args@~2.0.0:    version "2.0.1"    resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -5004,16 +4565,11 @@ punycode@^1.3.2:    resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"    integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== -punycode@^2.1.0: +punycode@^2.1.0, punycode@^2.1.1:    version "2.1.1"    resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"    integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -q@^1.1.2: -  version "1.5.1" -  resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" -  integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw== -  qs@6.10.3:    version "6.10.3"    resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e" @@ -5069,43 +4625,49 @@ raw-body@2.5.1:      iconv-lite "0.4.24"      unpipe "1.0.0" -react-dom@^17.0.1: -  version "17.0.2" -  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" -  integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== +react-dom@18: +  version "18.2.0" +  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" +  integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==    dependencies:      loose-envify "^1.1.0" -    object-assign "^4.1.1" -    scheduler "^0.20.2" +    scheduler "^0.23.0" + +react-error-boundary@^3.1.4: +  version "3.1.4" +  resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-3.1.4.tgz#255db92b23197108757a888b01e5b729919abde0" +  integrity sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA== +  dependencies: +    "@babel/runtime" "^7.12.5" -react-is@^16.13.1: +react-is@^16.13.1, react-is@^16.7.0:    version "16.13.1"    resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"    integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -react-tools@~0.13.0: -  version "0.13.3" -  resolved "https://registry.yarnpkg.com/react-tools/-/react-tools-0.13.3.tgz#da6ac7d4d7777a59a5e951cf46e72fd4b6b40a2c" -  integrity sha512-lmdjIRNk2cVUdlF/dyy6oP0nG2qrlX5qKFYRtiC5zK5Sg5QqgUEOrcS7Jz+kPNeOj9OWT7NfrR/cDvbGGSjCyg== -  dependencies: -    commoner "^0.10.0" -    jstransform "^10.1.0" +react-is@^18.0.0: +  version "18.2.0" +  resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" +  integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== -react@^17.0.1: -  version "17.0.2" -  resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" -  integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== +react-redux@^8.0.2: +  version "8.0.2" +  resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-8.0.2.tgz#bc2a304bb21e79c6808e3e47c50fe1caf62f7aad" +  integrity sha512-nBwiscMw3NoP59NFCXFf02f8xdo+vSHT/uZ1ldDwF7XaTpzm+Phk97VT4urYBl5TYAPNVaFm12UHAEyzkpNzRA==    dependencies: -    loose-envify "^1.1.0" -    object-assign "^4.1.1" +    "@babel/runtime" "^7.12.1" +    "@types/hoist-non-react-statics" "^3.3.1" +    "@types/use-sync-external-store" "^0.0.3" +    hoist-non-react-statics "^3.3.2" +    react-is "^18.0.0" +    use-sync-external-store "^1.0.0" -reactify@^1.1.1: -  version "1.1.1" -  resolved "https://registry.yarnpkg.com/reactify/-/reactify-1.1.1.tgz#a8f119596273c0d4bfb1abea0c14c2601ea03bba" -  integrity sha512-pWjYX2sqGD5ojOeSJMgkZngDFzCsp4gyIPJmkUfaKnCg4yHA9SGGtI3VZSSc4jD8R5WcPWXSC8qGszOE5uj6gQ== +react@18: +  version "18.2.0" +  resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" +  integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==    dependencies: -    react-tools "~0.13.0" -    through "~2.3.4" +    loose-envify "^1.1.0"  read-cache@^1.0.0:    version "1.0.0" @@ -5177,20 +4739,32 @@ readdirp@~3.6.0:    dependencies:      picomatch "^2.2.1" -recast@^0.11.17: -  version "0.11.23" -  resolved "https://registry.yarnpkg.com/recast/-/recast-0.11.23.tgz#451fd3004ab1e4df9b4e4b66376b2a21912462d3" -  integrity sha512-+nixG+3NugceyR8O1bLU45qs84JgI3+8EauyRZafLgC9XbdAOIVgwV1Pe2da0YzGo62KzWoZwUpVEQf6qNAXWA== +redux-devtools-extension@^2.13.9: +  version "2.13.9" +  resolved "https://registry.yarnpkg.com/redux-devtools-extension/-/redux-devtools-extension-2.13.9.tgz#6b764e8028b507adcb75a1cae790f71e6be08ae7" +  integrity sha512-cNJ8Q/EtjhQaZ71c8I9+BPySIBVEKssbPpskBfsXqb8HJ002A3KRVHfeRzwRo6mGPqsm7XuHTqNSNeS1Khig0A== + +redux-persist@^6.0.0: +  version "6.0.0" +  resolved "https://registry.yarnpkg.com/redux-persist/-/redux-persist-6.0.0.tgz#b4d2972f9859597c130d40d4b146fecdab51b3a8" +  integrity sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ== + +redux-thunk@^2.4.1: +  version "2.4.1" +  resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.1.tgz#0dd8042cf47868f4b29699941de03c9301a75714" +  integrity sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q== + +redux@^4.1.2: +  version "4.2.0" +  resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.0.tgz#46f10d6e29b6666df758780437651eeb2b969f13" +  integrity sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==    dependencies: -    ast-types "0.9.6" -    esprima "~3.1.0" -    private "~0.1.5" -    source-map "~0.5.0" +    "@babel/runtime" "^7.9.2" -regenerate-unicode-properties@^10.0.1: -  version "10.0.1" -  resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz#7f442732aa7934a3740c779bb9b3340dccc1fb56" -  integrity sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw== +regenerate-unicode-properties@^10.1.0: +  version "10.1.0" +  resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" +  integrity sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==    dependencies:      regenerate "^1.4.2" @@ -5211,14 +4785,6 @@ regenerator-transform@^0.15.0:    dependencies:      "@babel/runtime" "^7.8.4" -regex-not@^1.0.0, regex-not@^1.0.2: -  version "1.0.2" -  resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" -  integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== -  dependencies: -    extend-shallow "^3.0.2" -    safe-regex "^1.1.0" -  regexp.prototype.flags@^1.4.1, regexp.prototype.flags@^1.4.3:    version "1.4.3"    resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" @@ -5234,26 +4800,26 @@ regexpp@^3.1.0:    integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==  regexpu-core@^5.1.0: -  version "5.1.0" -  resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.1.0.tgz#2f8504c3fd0ebe11215783a41541e21c79942c6d" -  integrity sha512-bb6hk+xWd2PEOkj5It46A16zFMs2mv86Iwpdu94la4S3sJ7C973h2dHpYKwIBGaWSO7cIRJ+UX0IeMaWcO4qwA== +  version "5.2.1" +  resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.2.1.tgz#a69c26f324c1e962e9ffd0b88b055caba8089139" +  integrity sha512-HrnlNtpvqP1Xkb28tMhBUO2EbyUHdQlsnlAhzWcwHy8WJR53UWr7/MAvqrsQKMbV4qdpv03oTMG8iIhfsPFktQ==    dependencies:      regenerate "^1.4.2" -    regenerate-unicode-properties "^10.0.1" -    regjsgen "^0.6.0" -    regjsparser "^0.8.2" +    regenerate-unicode-properties "^10.1.0" +    regjsgen "^0.7.1" +    regjsparser "^0.9.1"      unicode-match-property-ecmascript "^2.0.0"      unicode-match-property-value-ecmascript "^2.0.0" -regjsgen@^0.6.0: -  version "0.6.0" -  resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.6.0.tgz#83414c5354afd7d6627b16af5f10f41c4e71808d" -  integrity sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA== +regjsgen@^0.7.1: +  version "0.7.1" +  resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.7.1.tgz#ee5ef30e18d3f09b7c369b76e7c2373ed25546f6" +  integrity sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA== -regjsparser@^0.8.2: -  version "0.8.4" -  resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.8.4.tgz#8a14285ffcc5de78c5b95d62bbf413b6bc132d5f" -  integrity sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA== +regjsparser@^0.9.1: +  version "0.9.1" +  resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709" +  integrity sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==    dependencies:      jsesc "~0.5.0" @@ -5264,12 +4830,7 @@ reload-css@^1.0.0:    dependencies:      query-string "^4.2.3" -repeat-element@^1.1.2: -  version "1.1.4" -  resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" -  integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== - -repeat-string@^1.5.2, repeat-string@^1.5.4, repeat-string@^1.6.1: +repeat-string@^1.5.2, repeat-string@^1.5.4:    version "1.6.1"    resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"    integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== @@ -5279,16 +4840,16 @@ require-from-string@^2.0.2:    resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"    integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== +reselect@^4.1.5: +  version "4.1.6" +  resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.6.tgz#19ca2d3d0b35373a74dc1c98692cdaffb6602656" +  integrity sha512-ZovIuXqto7elwnxyXbBtCPo9YFEr3uJqj2rRbcOOog1bmu2Ag85M4hixSwFWyaBMKXNgvPaJ9OSu9SkBPIeJHQ== +  resolve-from@^4.0.0:    version "4.0.0"    resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"    integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== -resolve-url@^0.2.1: -  version "0.2.1" -  resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" -  integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== -  resolve@^1.1.4, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.4.0:    version "1.22.1"    resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" @@ -5315,11 +4876,6 @@ resp-modifier@^6.0.0:      debug "^2.2.0"      minimatch "^3.0.2" -ret@~0.1.10: -  version "0.1.15" -  resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" -  integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== -  reversepoint@~0.2.0:    version "0.2.1"    resolved "https://registry.yarnpkg.com/reversepoint/-/reversepoint-0.2.1.tgz#d2ac3ff4d665cf0ff72296b7a78ee7237f6593f5" @@ -5355,25 +4911,17 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1:    resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"    integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-regex@^1.1.0: -  version "1.1.0" -  resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" -  integrity sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg== -  dependencies: -    ret "~0.1.10" -  "safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.1.0:    version "2.1.2"    resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"    integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -scheduler@^0.20.2: -  version "0.20.2" -  resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" -  integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== +scheduler@^0.23.0: +  version "0.23.0" +  resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" +  integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==    dependencies:      loose-envify "^1.1.0" -    object-assign "^4.1.1"  scope-analyzer@^2.0.1:    version "2.1.2" @@ -5388,11 +4936,6 @@ scope-analyzer@^2.0.1:      estree-is-function "^1.0.0"      get-assigned-identifiers "^1.1.0" -semver@7.0.0: -  version "7.0.0" -  resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" -  integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== -  semver@^5.5.0:    version "5.7.1"    resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" @@ -5439,16 +4982,6 @@ serve-static@1.15.0, serve-static@^1.10.0:      parseurl "~1.3.3"      send "0.18.0" -set-value@^2.0.0, set-value@^2.0.1: -  version "2.0.1" -  resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" -  integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== -  dependencies: -    extend-shallow "^2.0.1" -    is-extendable "^0.1.1" -    is-plain-object "^2.0.3" -    split-string "^3.0.1" -  setprototypeof@1.2.0:    version "1.2.0"    resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" @@ -5546,52 +5079,11 @@ slice-ansi@^4.0.0:      astral-regex "^2.0.0"      is-fullwidth-code-point "^3.0.0" -snapdragon-node@^2.0.1: -  version "2.1.1" -  resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" -  integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== -  dependencies: -    define-property "^1.0.0" -    isobject "^3.0.0" -    snapdragon-util "^3.0.1" - -snapdragon-util@^3.0.1: -  version "3.0.1" -  resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" -  integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== -  dependencies: -    kind-of "^3.2.0" - -snapdragon@^0.8.1: -  version "0.8.2" -  resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" -  integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== -  dependencies: -    base "^0.11.1" -    debug "^2.2.0" -    define-property "^0.2.5" -    extend-shallow "^2.0.1" -    map-cache "^0.2.2" -    source-map "^0.5.6" -    source-map-resolve "^0.5.0" -    use "^3.1.0" -  source-map-js@^1.0.2:    version "1.0.2"    resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"    integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== -source-map-resolve@^0.5.0: -  version "0.5.3" -  resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" -  integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== -  dependencies: -    atob "^2.1.2" -    decode-uri-component "^0.2.0" -    resolve-url "^0.2.1" -    source-map-url "^0.4.0" -    urix "^0.1.0" -  source-map-support@~0.5.10:    version "0.5.21"    resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" @@ -5600,19 +5092,7 @@ source-map-support@~0.5.10:      buffer-from "^1.0.0"      source-map "^0.6.0" -source-map-url@^0.4.0: -  version "0.4.1" -  resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" -  integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== - -source-map@0.1.31: -  version "0.1.31" -  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.31.tgz#9f704d0d69d9e138a81badf6ebb4fde33d151c61" -  integrity sha512-qFALUiKHo35Duky0Ubmb5YKj9b3c6CcgGNGeI60sd6Nn3KaY7h9fclEOcCVk0hwszwYYP6+X2/jpS5hHqqVuig== -  dependencies: -    amdefine ">=0.0.4" - -source-map@^0.5.6, source-map@~0.5.0, source-map@~0.5.3: +source-map@^0.5.6, source-map@~0.5.3:    version "0.5.7"    resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"    integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== @@ -5639,13 +5119,6 @@ split-filter-n@^1.1.2:    resolved "https://registry.yarnpkg.com/split-filter-n/-/split-filter-n-1.1.3.tgz#c983ae1e52402e70071f711a7af767a91f09f740"    integrity sha512-EU0EjvBI/mYBQMSAHq+ua/YNCuThuDjbU5h036k01+xieFW1aNvLNKb90xLihXIz5xJQX4VkEKan4LjSIyv7lg== -split-string@^3.0.1, split-string@^3.0.2: -  version "3.1.0" -  resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" -  integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== -  dependencies: -    extend-shallow "^3.0.0" -  split2@^0.2.1:    version "0.2.1"    resolved "https://registry.yarnpkg.com/split2/-/split2-0.2.1.tgz#02ddac9adc03ec0bb78c1282ec079ca6e85ae900" @@ -5670,14 +5143,6 @@ static-eval@^2.0.5:    dependencies:      escodegen "^1.11.1" -static-extend@^0.1.1: -  version "0.1.2" -  resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" -  integrity sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g== -  dependencies: -    define-property "^0.2.5" -    object-copy "^0.1.0" -  static-module@^3.0.0:    version "3.0.4"    resolved "https://registry.yarnpkg.com/static-module/-/static-module-3.0.4.tgz#bfbd1d1c38dd1fbbf0bb4af0c1b3ae18a93a2b68" @@ -6019,21 +5484,6 @@ to-fast-properties@^2.0.0:    resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"    integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== -to-object-path@^0.3.0: -  version "0.3.0" -  resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" -  integrity sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg== -  dependencies: -    kind-of "^3.0.2" - -to-regex-range@^2.1.0: -  version "2.1.1" -  resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" -  integrity sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg== -  dependencies: -    is-number "^3.0.0" -    repeat-string "^1.6.1" -  to-regex-range@^5.0.1:    version "5.0.1"    resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -6041,16 +5491,6 @@ to-regex-range@^5.0.1:    dependencies:      is-number "^7.0.0" -to-regex@^3.0.1, to-regex@^3.0.2: -  version "3.0.2" -  resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" -  integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== -  dependencies: -    define-property "^2.0.2" -    extend-shallow "^3.0.2" -    regex-not "^1.0.2" -    safe-regex "^1.1.0" -  toidentifier@1.0.1:    version "1.0.1"    resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" @@ -6093,7 +5533,7 @@ type@^1.0.1:    resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0"    integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== -type@^2.5.0: +type@^2.7.2:    version "2.7.2"    resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0"    integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw== @@ -6159,19 +5599,9 @@ unicode-match-property-value-ecmascript@^2.0.0:    integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==  unicode-property-aliases-ecmascript@^2.0.0: -  version "2.0.0" -  resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" -  integrity sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ== - -union-value@^1.0.0: -  version "1.0.1" -  resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" -  integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== -  dependencies: -    arr-union "^3.1.0" -    get-value "^2.0.6" -    is-extendable "^0.1.1" -    set-value "^2.0.1" +  version "2.1.0" +  resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" +  integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==  uniq@^1.0.1:    version "1.0.1" @@ -6183,18 +5613,10 @@ unpipe@1.0.0, unpipe@~1.0.0:    resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"    integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== -unset-value@^1.0.0: -  version "1.0.0" -  resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" -  integrity sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ== -  dependencies: -    has-value "^0.3.1" -    isobject "^3.0.0" - -update-browserslist-db@^1.0.5: -  version "1.0.5" -  resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz#be06a5eedd62f107b7c19eb5bcefb194411abf38" -  integrity sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q== +update-browserslist-db@^1.0.9: +  version "1.0.9" +  resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz#2924d3927367a38d5c555413a7ce138fc95fcb18" +  integrity sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==    dependencies:      escalade "^3.1.1"      picocolors "^1.0.0" @@ -6206,11 +5628,6 @@ uri-js@^4.2.2:    dependencies:      punycode "^2.1.0" -urix@^0.1.0: -  version "0.1.0" -  resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" -  integrity sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg== -  url-trim@^1.0.0:    version "1.0.0"    resolved "https://registry.yarnpkg.com/url-trim/-/url-trim-1.0.0.tgz#40057e2f164b88e5daca7269da47e6d1dd837adc" @@ -6224,10 +5641,10 @@ url@~0.11.0:      punycode "1.3.2"      querystring "0.2.0" -use@^3.1.0: -  version "3.1.1" -  resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" -  integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== +use-sync-external-store@^1.0.0: +  version "1.2.0" +  resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" +  integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==  util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:    version "1.0.2" @@ -6290,10 +5707,10 @@ vm-browserify@^1.0.0:    resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"    integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== -watchify-middleware@^1.8.2: -  version "1.9.0" -  resolved "https://registry.yarnpkg.com/watchify-middleware/-/watchify-middleware-1.9.0.tgz#27d1964687bbd9385a4e57a39fef0d06d32a1267" -  integrity sha512-KI+MJlzbgl/FgnjY+9VTn3uZMhZKv6SXTBXcN3pxbA1KUt67IMOx4WANng5RUoZ2y9+496guR8CMxDsfSyM2KA== +watchify-middleware@^1.9.1: +  version "1.9.1" +  resolved "https://registry.yarnpkg.com/watchify-middleware/-/watchify-middleware-1.9.1.tgz#3c6b9b18507a54a86c58b6eb5629f94cf9a116d4" +  integrity sha512-vjD5S1cTJC99ZLvq61AGiR+Es+4Oloo3mTzPvAPArlBlq8w2IJ1qtQheRgf26ihqjZ3qW/IfgLeqxWOyHorHbw==    dependencies:      concat-stream "^1.5.0"      debounce "^1.0.0" @@ -6357,6 +5774,11 @@ word-wrap@^1.2.3, word-wrap@~1.2.3:    resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"    integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== +wouter@^2.8.0-alpha.2: +  version "2.8.0-alpha.2" +  resolved "https://registry.yarnpkg.com/wouter/-/wouter-2.8.0-alpha.2.tgz#d57dfbd23b964b8bd848f2ed3eb2b38cf3c00e00" +  integrity sha512-aPsL5m5rW9RiceClOmGj6t5gn9Ut2TJVr98UDi1u9MIRNYiYVflg6vFIjdDYJ4IAyH0JdnkSgGwfo0LQS3k2zg== +  wrappy@1:    version "1.0.2"    resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" | 
