gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 938328cd077d40b75e0834d56ff8d43ad035fd2b
parent 2f22780800915b5d5262aea1754ecce44f752db7
Author: f0x52 <f0x@cthu.lu>
Date:   Thu, 29 Sep 2022 12:02:41 +0200

[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:
Dinternal/web/panels.go | 77-----------------------------------------------------------------------------
Minternal/web/profile.go | 1+
Ainternal/web/settings-panel.go | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Minternal/web/thread.go | 1+
Minternal/web/web.go | 30+++++++++++++++++-------------
Mweb/source/css/_colors.css | 102++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Mweb/source/css/base.css | 75++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Mweb/source/css/profile.css | 16++++++++--------
Mweb/source/css/status.css | 96++++++++++++++++++++++++++++++++++++-------------------------------------------
Mweb/source/dev-server.js | 24++++++++++++------------
Mweb/source/frontend/index.js | 5-----
Mweb/source/index.js | 52++++++++++++++++++++++++++++++++--------------------
Mweb/source/lib/split-css.js | 24++++++++++++------------
Dweb/source/lib/submit.js | 30------------------------------
Mweb/source/package.json | 27+++++++++++++++++++--------
Dweb/source/panels/admin/README.md | 22----------------------
Dweb/source/panels/admin/blocks.js | 319-------------------------------------------------------------------------------
Dweb/source/panels/admin/index.js | 65-----------------------------------------------------------------
Dweb/source/panels/admin/settings.js | 183-------------------------------------------------------------------------------
Dweb/source/panels/admin/style.css | 106-------------------------------------------------------------------------------
Dweb/source/panels/base.css | 67-------------------------------------------------------------------
Dweb/source/panels/lib/oauth.js | 227-------------------------------------------------------------------------------
Dweb/source/panels/lib/panel.js | 135-------------------------------------------------------------------------------
Dweb/source/panels/user/basic.js | 151------------------------------------------------------------------------------
Dweb/source/panels/user/index.js | 77-----------------------------------------------------------------------------
Dweb/source/panels/user/languages.js | 98-------------------------------------------------------------------------------
Dweb/source/panels/user/posts.js | 107-------------------------------------------------------------------------------
Dweb/source/panels/user/security.js | 80-------------------------------------------------------------------------------
Dweb/source/panels/user/style.css | 118-------------------------------------------------------------------------------
Aweb/source/settings-panel/admin/actions.js | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aweb/source/settings-panel/admin/emoji.js | 213+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aweb/source/settings-panel/admin/federation.js | 383+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aweb/source/settings-panel/admin/settings.js | 111+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aweb/source/settings-panel/components/error.jsx | 46++++++++++++++++++++++++++++++++++++++++++++++
Aweb/source/settings-panel/components/fake-toot.jsx | 44++++++++++++++++++++++++++++++++++++++++++++
Aweb/source/settings-panel/components/form-fields.jsx | 168+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aweb/source/settings-panel/components/languages.jsx | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aweb/source/settings-panel/components/login.jsx | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aweb/source/settings-panel/components/nav-button.jsx | 34++++++++++++++++++++++++++++++++++
Aweb/source/settings-panel/components/submit.jsx | 35+++++++++++++++++++++++++++++++++++
Aweb/source/settings-panel/index.js | 179+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aweb/source/settings-panel/lib/api/admin.js | 193+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aweb/source/settings-panel/lib/api/index.js | 186+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aweb/source/settings-panel/lib/api/oauth.js | 125+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aweb/source/settings-panel/lib/api/user.js | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aweb/source/settings-panel/lib/errors.js | 28++++++++++++++++++++++++++++
Aweb/source/settings-panel/lib/get-views.js | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aweb/source/settings-panel/lib/panel.js | 135+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aweb/source/settings-panel/lib/submit.js | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Aweb/source/settings-panel/redux/index.js | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Aweb/source/settings-panel/redux/reducers/admin.js | 132+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aweb/source/settings-panel/redux/reducers/instances.js | 43+++++++++++++++++++++++++++++++++++++++++++
Aweb/source/settings-panel/redux/reducers/oauth.js | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Aweb/source/settings-panel/redux/reducers/temporary.js | 33+++++++++++++++++++++++++++++++++
Aweb/source/settings-panel/redux/reducers/user.js | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Aweb/source/settings-panel/style.css | 499+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aweb/source/settings-panel/user/profile.js | 114+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aweb/source/settings-panel/user/settings.js | 141+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mweb/source/yarn.lock | 1514+++++++++++++++++++++++++------------------------------------------------------
Mweb/template/frontend.tmpl | 3++-
Mweb/template/index.tmpl | 2+-
Mweb/template/status.tmpl | 5++++-
62 files changed, 4272 insertions(+), 3099 deletions(-)

diff --git a/internal/web/panels.go b/internal/web/panels.go @@ -1,77 +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/>. -*/ - -package web - -import ( - "net/http" - - "github.com/gin-gonic/gin" - "github.com/superseriousbusiness/gotosocial/internal/api" - "github.com/superseriousbusiness/gotosocial/internal/config" - "github.com/superseriousbusiness/gotosocial/internal/gtserror" -) - -func (m *Module) UserPanelHandler(c *gin.Context) { - host := config.GetHost() - instance, err := m.processor.InstanceGet(c.Request.Context(), host) - if err != nil { - api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet) - return - } - - c.HTML(http.StatusOK, "frontend.tmpl", gin.H{ - "instance": instance, - "stylesheets": []string{ - assetsPathPrefix + "/Fork-Awesome/css/fork-awesome.min.css", - assetsPathPrefix + "/dist/_colors.css", - assetsPathPrefix + "/dist/base.css", - assetsPathPrefix + "/dist/panels-base.css", - assetsPathPrefix + "/dist/panels-user-style.css", - }, - "javascript": []string{ - assetsPathPrefix + "/dist/bundle.js", - assetsPathPrefix + "/dist/user-panel.js", - }, - }) -} - -// TODO: abstract the {admin, user}panel handlers in some way -func (m *Module) AdminPanelHandler(c *gin.Context) { - host := config.GetHost() - instance, err := m.processor.InstanceGet(c.Request.Context(), host) - if err != nil { - api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet) - return - } - - c.HTML(http.StatusOK, "frontend.tmpl", gin.H{ - "instance": instance, - "stylesheets": []string{ - assetsPathPrefix + "/Fork-Awesome/css/fork-awesome.min.css", - assetsPathPrefix + "/dist/_colors.css", - assetsPathPrefix + "/dist/base.css", - assetsPathPrefix + "/dist/panels-base.css", - assetsPathPrefix + "/dist/panels-admin-style.css", - }, - "javascript": []string{ - assetsPathPrefix + "/dist/bundle.js", - assetsPathPrefix + "/dist/admin-panel.js", - }, - }) -} diff --git a/internal/web/profile.go b/internal/web/profile.go @@ -117,6 +117,7 @@ func (m *Module) profileGETHandler(c *gin.Context) { "show_back_to_top": showBackToTop, "stylesheets": stylesheets, "javascript": []string{ + "/assets/dist/bundle.js", "/assets/dist/frontend.js", }, }) diff --git a/internal/web/settings-panel.go b/internal/web/settings-panel.go @@ -0,0 +1,53 @@ +/* + 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/>. +*/ + +package web + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/api" + "github.com/superseriousbusiness/gotosocial/internal/config" + "github.com/superseriousbusiness/gotosocial/internal/gtserror" +) + +func (m *Module) SettingsPanelHandler(c *gin.Context) { + host := config.GetHost() + instance, err := m.processor.InstanceGet(c.Request.Context(), host) + if err != nil { + api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet) + return + } + + c.HTML(http.StatusOK, "frontend.tmpl", gin.H{ + "instance": instance, + "stylesheets": []string{ + assetsPathPrefix + "/Fork-Awesome/css/fork-awesome.min.css", + assetsPathPrefix + "/dist/_colors.css", + assetsPathPrefix + "/dist/base.css", + assetsPathPrefix + "/dist/profile.css", + assetsPathPrefix + "/dist/status.css", + assetsPathPrefix + "/dist/settings-panel-style.css", + }, + "javascript": []string{ + assetsPathPrefix + "/dist/bundle.js", + assetsPathPrefix + "/dist/settings.js", + }, + }) +} diff --git a/internal/web/thread.go b/internal/web/thread.go @@ -119,6 +119,7 @@ func (m *Module) threadGETHandler(c *gin.Context) { "ogMeta": ogBase(instance).withStatus(status), "stylesheets": stylesheets, "javascript": []string{ + "/assets/dist/bundle.js", "/assets/dist/frontend.js", }, }) diff --git a/internal/web/web.go b/internal/web/web.go @@ -37,9 +37,9 @@ const ( profilePath = "/@:" + usernameKey customCSSPath = profilePath + "/custom.css" statusPath = profilePath + "/statuses/:" + statusIDKey - adminPanelPath = "/admin" - userPanelpath = "/user" assetsPathPrefix = "/assets" + userPanelPath = "/settings/user" + adminPanelPath = "/settings/admin" tokenParam = "token" usernameKey = "username" @@ -70,20 +70,24 @@ func (m *Module) Route(s router.Router) error { assetsGroup := s.AttachGroup(assetsPathPrefix) m.mountAssetsFilesystem(assetsGroup) - s.AttachHandler(http.MethodGet, adminPanelPath, m.AdminPanelHandler) - // redirect /admin/ to /admin - s.AttachHandler(http.MethodGet, adminPanelPath+"/", func(c *gin.Context) { - c.Redirect(http.StatusMovedPermanently, adminPanelPath) + s.AttachHandler(http.MethodGet, "/settings", m.SettingsPanelHandler) + s.AttachHandler(http.MethodGet, "/settings/*panel", m.SettingsPanelHandler) + + // User panel redirects + // used by clients + s.AttachHandler(http.MethodGet, "/auth/edit", func(c *gin.Context) { + c.Redirect(http.StatusMovedPermanently, userPanelPath) }) - s.AttachHandler(http.MethodGet, userPanelpath, m.UserPanelHandler) - // redirect /user/ to /user - s.AttachHandler(http.MethodGet, userPanelpath+"/", func(c *gin.Context) { - c.Redirect(http.StatusMovedPermanently, userPanelpath) + // old version of settings panel + s.AttachHandler(http.MethodGet, "/user", func(c *gin.Context) { + c.Redirect(http.StatusMovedPermanently, userPanelPath) }) - // redirect /auth/edit to /user - s.AttachHandler(http.MethodGet, "/auth/edit", func(c *gin.Context) { - c.Redirect(http.StatusMovedPermanently, userPanelpath) + + // Admin panel redirects + // old version of settings panel + s.AttachHandler(http.MethodGet, "/admin", func(c *gin.Context) { + c.Redirect(http.StatusMovedPermanently, adminPanelPath) }) // serve front-page diff --git 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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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/lib/panel.js b/web/source/panels/lib/panel.js @@ -1,134 +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 oauthLib = require("./oauth"); - -module.exports = function createPanel(clientName, scope, Component) { - ReactDom.render(<Panel/>, document.getElementById("root")); - - function Panel() { - const [oauth, setOauth] = React.useState(); - const [hasAuth, setAuth] = React.useState(false); - const [oauthState, setOauthState] = React.useState(localStorage.getItem("oauth")); - - React.useEffect(() => { - let state = localStorage.getItem("oauth"); - if (state != undefined) { - state = JSON.parse(state); - let restoredOauth = oauthLib(state.config, state); - Promise.try(() => { - return restoredOauth.callback(); - }).then(() => { - setAuth(true); - }); - setOauth(restoredOauth); - } - }, [setAuth, setOauth]); - - if (!hasAuth && oauth && oauth.isAuthorized()) { - setAuth(true); - } - - if (oauth && oauth.isAuthorized()) { - return <Component oauth={oauth} />; - } else if (oauthState != undefined) { - return "processing oauth..."; - } else { - return <Auth setOauth={setOauth} />; - } - } - - function Auth({setOauth}) { - const [ instance, setInstance ] = React.useState(""); - - React.useEffect(() => { - let isStillMounted = true; - // check if current domain runs an instance - let thisUrl = new URL(window.location.origin); - thisUrl.pathname = "/api/v1/instance"; - Promise.try(() => { - return fetch(thisUrl.href); - }).then((res) => { - if (res.status == 200) { - return res.json(); - } - }).then((json) => { - if (json && json.uri && isStillMounted) { - setInstance(json.uri); - } - }).catch((e) => { - console.log("error checking instance response:", e); - }); - - return () => { - // cleanup function - isStillMounted = false; - }; - }, []); - - function doAuth() { - return Promise.try(() => { - return new URL(instance); - }).catch(TypeError, () => { - return new URL(`https://${instance}`); - }).then((parsedURL) => { - let url = parsedURL.toString(); - let oauth = oauthLib({ - instance: url, - client_name: clientName, - scope: scope, - website: window.location.href - }); - setOauth(oauth); - setInstance(url); - return oauth.register().then(() => { - return oauth; - }); - }).then((oauth) => { - return oauth.authorize(); - }).catch((e) => { - console.log("error authenticating:", e); - }); - } - - function updateInstance(e) { - if (e.key == "Enter") { - doAuth(); - } else { - setInstance(e.target.value); - } - } - - return ( - <section className="login"> - <h1>OAUTH Login:</h1> - <form onSubmit={(e) => e.preventDefault()}> - <label htmlFor="instance">Instance: </label> - <input value={instance} onChange={updateInstance} id="instance"/> - <button onClick={doAuth}>Authenticate</button> - </form> - </section> - ); - } -}; -\ No newline at end of file diff --git a/web/source/panels/user/basic.js b/web/source/panels/user/basic.js @@ -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}&apos;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 @@ -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/languages.js b/web/source/panels/user/languages.js @@ -1,98 +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 Languages() { - return <React.Fragment> - <option value="AF">Afrikaans</option> - <option value="SQ">Albanian</option> - <option value="AR">Arabic</option> - <option value="HY">Armenian</option> - <option value="EU">Basque</option> - <option value="BN">Bengali</option> - <option value="BG">Bulgarian</option> - <option value="CA">Catalan</option> - <option value="KM">Cambodian</option> - <option value="ZH">Chinese (Mandarin)</option> - <option value="HR">Croatian</option> - <option value="CS">Czech</option> - <option value="DA">Danish</option> - <option value="NL">Dutch</option> - <option value="EN">English</option> - <option value="ET">Estonian</option> - <option value="FJ">Fiji</option> - <option value="FI">Finnish</option> - <option value="FR">French</option> - <option value="KA">Georgian</option> - <option value="DE">German</option> - <option value="EL">Greek</option> - <option value="GU">Gujarati</option> - <option value="HE">Hebrew</option> - <option value="HI">Hindi</option> - <option value="HU">Hungarian</option> - <option value="IS">Icelandic</option> - <option value="ID">Indonesian</option> - <option value="GA">Irish</option> - <option value="IT">Italian</option> - <option value="JA">Japanese</option> - <option value="JW">Javanese</option> - <option value="KO">Korean</option> - <option value="LA">Latin</option> - <option value="LV">Latvian</option> - <option value="LT">Lithuanian</option> - <option value="MK">Macedonian</option> - <option value="MS">Malay</option> - <option value="ML">Malayalam</option> - <option value="MT">Maltese</option> - <option value="MI">Maori</option> - <option value="MR">Marathi</option> - <option value="MN">Mongolian</option> - <option value="NE">Nepali</option> - <option value="NO">Norwegian</option> - <option value="FA">Persian</option> - <option value="PL">Polish</option> - <option value="PT">Portuguese</option> - <option value="PA">Punjabi</option> - <option value="QU">Quechua</option> - <option value="RO">Romanian</option> - <option value="RU">Russian</option> - <option value="SM">Samoan</option> - <option value="SR">Serbian</option> - <option value="SK">Slovak</option> - <option value="SL">Slovenian</option> - <option value="ES">Spanish</option> - <option value="SW">Swahili</option> - <option value="SV">Swedish </option> - <option value="TA">Tamil</option> - <option value="TT">Tatar</option> - <option value="TE">Telugu</option> - <option value="TH">Thai</option> - <option value="BO">Tibetan</option> - <option value="TO">Tonga</option> - <option value="TR">Turkish</option> - <option value="UK">Ukrainian</option> - <option value="UR">Urdu</option> - <option value="UZ">Uzbek</option> - <option value="VI">Vietnamese</option> - <option value="CY">Welsh</option> - <option value="XH">Xhosa</option> - </React.Fragment>; -}; diff --git a/web/source/panels/user/posts.js b/web/source/panels/user/posts.js @@ -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 @@ -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 @@ -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 @@ -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 @@ -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&apos;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 @@ -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">&lt; 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 @@ -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 @@ -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 @@ -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 @@ -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/settings-panel/components/languages.jsx b/web/source/settings-panel/components/languages.jsx @@ -0,0 +1,98 @@ +/* + 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 Languages() { + return <React.Fragment> + <option value="AF">Afrikaans</option> + <option value="SQ">Albanian</option> + <option value="AR">Arabic</option> + <option value="HY">Armenian</option> + <option value="EU">Basque</option> + <option value="BN">Bengali</option> + <option value="BG">Bulgarian</option> + <option value="CA">Catalan</option> + <option value="KM">Cambodian</option> + <option value="ZH">Chinese (Mandarin)</option> + <option value="HR">Croatian</option> + <option value="CS">Czech</option> + <option value="DA">Danish</option> + <option value="NL">Dutch</option> + <option value="EN">English</option> + <option value="ET">Estonian</option> + <option value="FJ">Fiji</option> + <option value="FI">Finnish</option> + <option value="FR">French</option> + <option value="KA">Georgian</option> + <option value="DE">German</option> + <option value="EL">Greek</option> + <option value="GU">Gujarati</option> + <option value="HE">Hebrew</option> + <option value="HI">Hindi</option> + <option value="HU">Hungarian</option> + <option value="IS">Icelandic</option> + <option value="ID">Indonesian</option> + <option value="GA">Irish</option> + <option value="IT">Italian</option> + <option value="JA">Japanese</option> + <option value="JW">Javanese</option> + <option value="KO">Korean</option> + <option value="LA">Latin</option> + <option value="LV">Latvian</option> + <option value="LT">Lithuanian</option> + <option value="MK">Macedonian</option> + <option value="MS">Malay</option> + <option value="ML">Malayalam</option> + <option value="MT">Maltese</option> + <option value="MI">Maori</option> + <option value="MR">Marathi</option> + <option value="MN">Mongolian</option> + <option value="NE">Nepali</option> + <option value="NO">Norwegian</option> + <option value="FA">Persian</option> + <option value="PL">Polish</option> + <option value="PT">Portuguese</option> + <option value="PA">Punjabi</option> + <option value="QU">Quechua</option> + <option value="RO">Romanian</option> + <option value="RU">Russian</option> + <option value="SM">Samoan</option> + <option value="SR">Serbian</option> + <option value="SK">Slovak</option> + <option value="SL">Slovenian</option> + <option value="ES">Spanish</option> + <option value="SW">Swahili</option> + <option value="SV">Swedish </option> + <option value="TA">Tamil</option> + <option value="TT">Tatar</option> + <option value="TE">Telugu</option> + <option value="TH">Thai</option> + <option value="BO">Tibetan</option> + <option value="TO">Tonga</option> + <option value="TR">Turkish</option> + <option value="UK">Ukrainian</option> + <option value="UR">Urdu</option> + <option value="UZ">Uzbek</option> + <option value="VI">Vietnamese</option> + <option value="CY">Welsh</option> + <option value="XH">Xhosa</option> + </React.Fragment>; +}; diff --git a/web/source/settings-panel/components/login.jsx 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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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/settings-panel/lib/panel.js b/web/source/settings-panel/lib/panel.js @@ -0,0 +1,134 @@ +/* + 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 oauthLib = require("./oauth"); + +module.exports = function createPanel(clientName, scope, Component) { + ReactDom.render(<Panel/>, document.getElementById("root")); + + function Panel() { + const [oauth, setOauth] = React.useState(); + const [hasAuth, setAuth] = React.useState(false); + const [oauthState, setOauthState] = React.useState(localStorage.getItem("oauth")); + + React.useEffect(() => { + let state = localStorage.getItem("oauth"); + if (state != undefined) { + state = JSON.parse(state); + let restoredOauth = oauthLib(state.config, state); + Promise.try(() => { + return restoredOauth.callback(); + }).then(() => { + setAuth(true); + }); + setOauth(restoredOauth); + } + }, [setAuth, setOauth]); + + if (!hasAuth && oauth && oauth.isAuthorized()) { + setAuth(true); + } + + if (oauth && oauth.isAuthorized()) { + return <Component oauth={oauth} />; + } else if (oauthState != undefined) { + return "processing oauth..."; + } else { + return <Auth setOauth={setOauth} />; + } + } + + function Auth({setOauth}) { + const [ instance, setInstance ] = React.useState(""); + + React.useEffect(() => { + let isStillMounted = true; + // check if current domain runs an instance + let thisUrl = new URL(window.location.origin); + thisUrl.pathname = "/api/v1/instance"; + Promise.try(() => { + return fetch(thisUrl.href); + }).then((res) => { + if (res.status == 200) { + return res.json(); + } + }).then((json) => { + if (json && json.uri && isStillMounted) { + setInstance(json.uri); + } + }).catch((e) => { + console.log("error checking instance response:", e); + }); + + return () => { + // cleanup function + isStillMounted = false; + }; + }, []); + + function doAuth() { + return Promise.try(() => { + return new URL(instance); + }).catch(TypeError, () => { + return new URL(`https://${instance}`); + }).then((parsedURL) => { + let url = parsedURL.toString(); + let oauth = oauthLib({ + instance: url, + client_name: clientName, + scope: scope, + website: window.location.href + }); + setOauth(oauth); + setInstance(url); + return oauth.register().then(() => { + return oauth; + }); + }).then((oauth) => { + return oauth.authorize(); + }).catch((e) => { + console.log("error authenticating:", e); + }); + } + + function updateInstance(e) { + if (e.key == "Enter") { + doAuth(); + } else { + setInstance(e.target.value); + } + } + + return ( + <section className="login"> + <h1>OAUTH Login:</h1> + <form onSubmit={(e) => e.preventDefault()}> + <label htmlFor="instance">Instance: </label> + <input value={instance} onChange={updateInstance} id="instance"/> + <button onClick={doAuth}>Authenticate</button> + </form> + </section> + ); + } +}; +\ No newline at end of file diff --git a/web/source/settings-panel/lib/submit.js 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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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" diff --git a/web/template/frontend.tmpl b/web/template/frontend.tmpl @@ -1,5 +1,6 @@ {{ template "header.tmpl" .}} <main class="lightgray"> - <div id="root"></div> + <div id="root"> + </div> </main> {{ template "footer.tmpl" .}} \ No newline at end of file diff --git a/web/template/index.tmpl b/web/template/index.tmpl @@ -1,5 +1,5 @@ {{ template "header.tmpl" .}} -<section class="excerpt_top"> +<section class="excerpt-top"> home to <span class="count">{{.instance.Stats.user_count}}</span> users who posted <span class="count">{{.instance.Stats.status_count}}</span> statuses, federating with <span class="count">{{.instance.Stats.domain_count}}</span> other instances. diff --git a/web/template/status.tmpl b/web/template/status.tmpl @@ -2,6 +2,10 @@ <a href="{{.Account.URL}}" class="avatar"><img src="{{.Account.Avatar}}" alt=""></a> <a href="{{.Account.URL}}" class="displayname">{{if .Account.DisplayName}}{{emojify .Account.Emojis (escape .Account.DisplayName)}}{{else}}{{.Account.Username}}{{end}}</a> <a href="{{.Account.URL}}" class="username">@{{.Account.Acct}}</a> + <div class="not-expanded"> + <span class="visibility">{{.Visibility | visibilityIcon}}</span> + <span class="date">{{.CreatedAt | timestamp}}</span> + </div> <div class="text"> {{if .SpoilerText}} <input class="spoiler" id="hideSpoiler-{{.ID}}" type="checkbox" style="display: none" aria-hidden="true" checked="true" /> @@ -43,7 +47,6 @@ <div class="info"> <div id="date">{{.CreatedAt | timestamp}}</div> <div class="stats"> - <div id="visibility">{{.Visibility | visibilityIcon}}</div> <div id="replies"><i aria-label="Replies" class="fa fa-reply-all"></i> {{.RepliesCount}}</div> <div id="boosts"><i aria-label="Boosts" class="fa fa-retweet"></i> {{.ReblogsCount}}</div> <div id="favorites"><i aria-label="Favorites" class="fa fa-star"></i> {{.FavouritesCount}}</div>