diff --git a/js/api.js b/js/api.js index afc81c7..a065064 100644 --- a/js/api.js +++ b/js/api.js @@ -340,6 +340,7 @@ const default_applicationstate = { }; var __store; +var applicationHydrated = false; export function initializeStore(initialState = default_applicationstate) { __store = createStore( @@ -348,7 +349,9 @@ export function initializeStore(initialState = default_applicationstate) { composeWithDevTools(applyMiddleware(thunkMiddleware)) ); __store.subscribe(() => { - localStorage.setItem('reduxState', JSON.stringify(__store.getState())); + if(applicationHydrated) { + localStorage.setItem('reduxState', JSON.stringify(__store.getState())); + } }); return __store; } @@ -449,11 +452,12 @@ function rehydrateApplicationState() { if(persistedState) { __store.dispatch({ type : actiontypes_userinfo.REHYDRATE, - parameters : Object.assign({}, persistedState.userinfo, {}) + parameters : Object.assign({}, persistedState.userinfo) }); __store.dispatch({ type : actiontypes_tournamentinfo.REHYDRATE, - parameters : Object.assign({}, persistedState.tournamentinfo, {}) + parameters : Object.assign({}, persistedState.tournamentinfo) }); + applicationHydrated = true; } } diff --git a/js/components/EditableStringList.js b/js/components/EditableStringList.js index 08bf284..38e7cf7 100644 --- a/js/components/EditableStringList.js +++ b/js/components/EditableStringList.js @@ -1,54 +1,243 @@ import React from 'react'; import { Alert, - Button, + Button, + Card, + CardBody, + CardTitle, Input, InputGroup, InputGroupAddon } from 'reactstrap'; +import '../../static/css/editablestringlist.css'; + export default class EditableStringList extends React.Component { + constructor(props) { super(props); this.state = { - entries: props.entries + groupSize: props.groupSize, + teams: props.teams, + groups: props.groups }; this.add = this.add.bind(this); this.remove = this.remove.bind(this); + this.onGroupSwitch = this.onGroupSwitch.bind(this); } add(text) { - if (text === '' || this.state.entries.includes(text)) { + if (text === '' || this.state.teams.includes(text)) { return false; } - this.state.entries.push(text); - this.setState({entries: this.state.entries}); - this.props.onChange(this.state.entries); + this.state.teams.push(text); + + var lastGroup = this.state.groups[this.state.groups.length - 1]; + if(lastGroup === undefined || lastGroup.length >= this.state.groupSize) { + this.state.groups[this.state.groups.length] = []; + } + lastGroup = this.state.groups[this.state.groups.length - 1]; + lastGroup[lastGroup.length] = text; + + this.setState({ + teams: this.state.teams, + groups: this.state.groups + }); + + this.props.onTeamsChange(this.state.teams); + this.props.onGroupsChange(this.state.groups); + return true; } remove(text) { - let tmp = this.state.entries.filter(item => item !== text); - this.setState({entries: tmp}); - this.props.onChange(tmp); + if(this.removeTeamFromGroup(text) === false) { + return false; + } + + this.setState({ + teams: this.state.teams, + groups: this.state.groups + }); + + this.props.onTeamsChange(this.state.teams); + this.props.onGroupsChange(this.state.groups); + } + + removeTeamFromGroup(text) { + this.state.teams = this.state.teams.filter(item => item !== text); + + let teamIndex = this.findTeam(text); + if(teamIndex === null) { + return false; + } + + // Move every first team to the next group + this.state.groups[teamIndex.group].splice(teamIndex.team, 1); + for(var group = teamIndex.group; group < this.state.groups.length - 1; group++) { + let currentGroup = this.state.groups[group]; + currentGroup[currentGroup.length] = this.state.groups[group + 1].splice(0, 1)[0]; + } + + // delete the last group in case it is empty + if(this.state.groups[this.state.groups.length - 1].length === 0) { + this.state.groups.splice(this.state.groups.length - 1, 1); + } + + return true; + } + + findTeam(text) { + for(var group = 0; group < this.state.groups.length; group++) { + for(var team = 0; team < this.state.groups[group].length; team++) { + if(this.state.groups[group][team] === text) { + return { + group: group, + team: team + }; + } + } + } + return null; + } + + resizeGroups(newSize) { + let oldGroups = this.state.groups; + var rearrangedGroups = []; + + for(var oldGroupIndex = 0; oldGroupIndex < oldGroups.length; oldGroupIndex++) { + for(var oldTeamIndex = 0; oldTeamIndex < oldGroups[oldGroupIndex].length; oldTeamIndex++) { + let index = oldGroupIndex * this.state.groupSize + oldTeamIndex; + + let newGroupIndex = Math.floor(index / newSize); + let newTeamIndex = index % newSize; + + if(newTeamIndex === 0) { + rearrangedGroups[newGroupIndex] = []; + } + + rearrangedGroups[newGroupIndex][newTeamIndex] = oldGroups[oldGroupIndex][oldTeamIndex]; + } + } + + this.setState({ + groupSize: newSize, + groups: rearrangedGroups + }); + this.props.onGroupsChange(this.state.groups); + } + + onGroupSwitch(src, dest) { + const groupCopy = this.state.groups.slice(); + + const srcTeam = groupCopy[src.group][src.team]; + const destTeam = groupCopy[dest.group][dest.team]; + + groupCopy[src.group].splice(src.team, 1, destTeam); + groupCopy[dest.group].splice(dest.team, 1, srcTeam); + + this.setState({ + groups: groupCopy + }); + this.props.onGroupsChange(this.state.groups); } render() { - if ((typeof this.state.entries !== 'undefined') && this.state.entries.length > 0) { - return ( -
- - {this.state.entries.map((text) => )} -
- ); - } else { - return ( -
- - {this.props.placeholder} -
- ); + if(this.props.groupSize !== this.state.groupSize) { + this.resizeGroups(this.props.groupSize); } + + if(this.props.groupPhaseEnabled) { + if ((typeof this.state.teams !== 'undefined') && this.state.teams.length > 0) { + return ( +
+ + +
+ ); + } else { + return ( +
+ + {this.props.groupPlaceHolder} +
+ ); + } + } else { + if ((typeof this.state.teams !== 'undefined') && this.state.teams.length > 0) { + return ( +
+ + {this.state.teams.map((text) => )} +
+ ); + } else { + return ( +
+ + {this.props.teamPlaceholder} +
+ ); + } + } + } +} + +class GroupView extends React.Component { + + constructor(props) { + super(props); + } + + render() { + return ( +
+ {this.props.groups.map((group, groupindex) => ( + + + Group {groupindex + 1} + {group.map((team, teamindex) => ( +
this.onDragStart(e, groupindex,teamindex)} + onDragOver={(e) => this.onDragOver(e)} + onDrop={(e) => this.onDrop(e, groupindex, teamindex)}> + + + +
+ ))} +
+
+ ))} +
+ ); + } + + onDragStart(e, group, team) { + e.dataTransfer.setData( + 'text/plain', + JSON.stringify({ + group: group, + team: team + }) + ); + } + + onDragOver(e) { + e.preventDefault(); + } + + onDrop(e, group, team) { + e.preventDefault(); + + let src = JSON.parse(e.dataTransfer.getData('text')); + let dest = { + group: group, + team: team + }; + + this.props.onGroupSwitched(src, dest); } } @@ -104,7 +293,7 @@ class Item extends React.Component { render() { return ( - + {this.props.text} ); diff --git a/package.json b/package.json index ae3ad3d..1d7d4fe 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "react-dom": "^16.6.1", "react-favicon": "^0.0.14", "react-notify-toast": "^0.5.0", + "react-pose": "^4.0.8", "react-redux": "^5.1.1", "reactstrap": "^6.5.0", "redux": "^4.0.1", diff --git a/pages/create.js b/pages/create.js index 2565c7c..8d079dd 100644 --- a/pages/create.js +++ b/pages/create.js @@ -2,6 +2,7 @@ import Head from 'next/head'; import React from 'react'; import { notify } from 'react-notify-toast'; import { connect } from 'react-redux'; +import posed from 'react-pose'; import { Button, @@ -9,7 +10,6 @@ import { CardBody, Container, CustomInput, - Fade, Form, FormGroup, Input, @@ -25,7 +25,7 @@ import { createTournament } from '../js/api'; import '../static/everypage.css'; -class PrivateCreatePage extends React.Component { +class CreatePage extends React.Component { render() { const { isSignedIn } = this.props; @@ -66,11 +66,9 @@ function mapStateToCreatePageProperties(state) { return { isSignedIn }; } -const CreatePage = connect( +export default connect( mapStateToCreatePageProperties -)(PrivateCreatePage); - -export default CreatePage; +)(CreatePage); function CreateTournamentCard() { return ( @@ -85,23 +83,41 @@ function CreateTournamentCard() { ); } +const GroupphaseFader = posed.div({ + visible: { + opacity: 1, + height: 150 + }, + hidden: { + opacity: 0, + height: 0 + } +}); + class CreateTournamentForm extends React.Component { constructor(props) { super(props); this.state = { - fadeIn: false, + groupPhaseEnabled: false, name: '', description: '', public: false, - teams: [] + + groupSize: 4, + groupAdvance: 1, + teams: [], + groups: [] }; - this.toggle = this.toggle.bind(this); + this.handleGroupPhaseEnabledInput = this.handleGroupPhaseEnabledInput.bind(this); this.teamListUpdate = this.teamListUpdate.bind(this); + this.groupListUpdate = this.groupListUpdate.bind(this); this.create = this.create.bind(this); this.handleNameInput = this.handleNameInput.bind(this); this.handleDescriptionInput = this.handleDescriptionInput.bind(this); this.handlePublicInput = this.handlePublicInput.bind(this); + this.handleGroupSizeInput = this.handleGroupSizeInput.bind(this); + this.handleGroupAdvanceInput = this.handleGroupAdvanceInput.bind(this); this.create = this.create.bind(this); } @@ -122,24 +138,35 @@ class CreateTournamentForm extends React.Component { - + - + - + - + - +

Teams

- + ); @@ -149,10 +176,28 @@ class CreateTournamentForm extends React.Component { this.setState({teams: list}); } - toggle() { - this.setState({ - fadeIn: !this.state.fadeIn - }); + groupListUpdate(list) { + this.setState({groups: list}); + } + + handleGroupSizeInput(input) { + let newSize = input.target.value; + if(newSize <= this.state.groupAdvance) { + this.setState({ + groupSize: newSize, + groupAdvance: newSize - 1 + }); + } else { + this.setState({ groupSize: newSize }); + } + } + + handleGroupAdvanceInput(input) { + this.setState({ groupAdvance: input.target.value }); + } + + handleGroupPhaseEnabledInput(input) { + this.setState({ groupPhaseEnabled: input.target.checked }); } handleNameInput(input) { diff --git a/static/css/editablestringlist.css b/static/css/editablestringlist.css new file mode 100644 index 0000000..b0012b0 --- /dev/null +++ b/static/css/editablestringlist.css @@ -0,0 +1,25 @@ + +.group-card { + margin-top: 15px; + margin-right: 15px; + display: inline-block; + vertical-align: top; +} + +.team-item { + display: inline-block; +} + +.grouped-team-item { + display: block; + margin-bottom: .5rem; + border-radius: .25rem; +} + +.grouped-team-item:last-child { + margin-bottom: 0px; +} + +.grouped-team-item > .m-2 { + margin: 0 !important; +} diff --git a/yarn.lock b/yarn.lock index ad27f6d..1a7c3c7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -715,6 +715,18 @@ lodash "^4.17.11" to-fast-properties "^2.0.0" +"@emotion/is-prop-valid@^0.7.3": + version "0.7.3" + resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.7.3.tgz#a6bf4fa5387cbba59d44e698a4680f481a8da6cc" + integrity sha512-uxJqm/sqwXw3YPA5GXX365OBcJGFtxUVkB6WyezqFHlNe9jqUWH5ur2O2M8dGBz61kn1g3ZBlzUunFQXQIClhA== + dependencies: + "@emotion/memoize" "0.7.1" + +"@emotion/memoize@0.7.1": + version "0.7.1" + resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.1.tgz#e93c13942592cf5ef01aa8297444dc192beee52f" + integrity sha512-Qv4LTqO11jepd5Qmlp3M1YEjBumoTHcHFdgPTQ+sFlIL5myi/7xu/POwP7IRu6odBdmLXdtIs1D6TuW6kbwbbg== + "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" @@ -728,6 +740,21 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== +"@popmotion/easing@^1.0.1": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@popmotion/easing/-/easing-1.0.2.tgz#17d925c45b4bf44189e5a38038d149df42d8c0b4" + integrity sha512-IkdW0TNmRnWTeWI7aGQIVDbKXPWHVEYdGgd5ZR4SH/Ty/61p63jCjrPxX1XrR7IGkl08bjhJROStD7j+RKgoIw== + +"@popmotion/popcorn@^0.3.6": + version "0.3.6" + resolved "https://registry.yarnpkg.com/@popmotion/popcorn/-/popcorn-0.3.6.tgz#eb4e3a04dd45c9516cc55b3350760ff47d7d55da" + integrity sha512-B8Hdk4LOjCHTIiUd9KfD+9PgLR2iSe9T/X5G9IAl055KY2iAqodIEtTlO6EBpjP8tQWVfI3C4A/fEf0RqYqPPw== + dependencies: + "@popmotion/easing" "^1.0.1" + framesync "^4.0.1" + hey-listen "^1.0.5" + style-value-types "^3.1.0" + "@samverschueren/stream-to-observable@^0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f" @@ -740,6 +767,16 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== +"@types/invariant@^2.2.29": + version "2.2.29" + resolved "https://registry.yarnpkg.com/@types/invariant/-/invariant-2.2.29.tgz#aa845204cd0a289f65d47e0de63a6a815e30cc66" + integrity sha512-lRVw09gOvgviOfeUrKc/pmTiRZ7g7oDOU6OAutyuSHpm1/o2RaBQvRhgK8QEdu+FFuw/wnWb29A/iuxv9i8OpQ== + +"@types/node@^10.0.5": + version "10.14.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.4.tgz#1c586b991457cbb58fef51bc4e0cfcfa347714b5" + integrity sha512-DT25xX/YgyPKiHFOpNuANIQIVvYEwCWXgK2jYYwqgaMrYE6+tq+DtmMwlD3drl6DJbUwtlIDnn0d7tIn/EbXBg== + "@webassemblyjs/ast@1.7.8": version "1.7.8" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.7.8.tgz#f31f480debeef957f01b623f27eabc695fa4fe8f" @@ -3703,6 +3740,13 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" +framesync@^4.0.0, framesync@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/framesync/-/framesync-4.0.2.tgz#b03b62852f12b0d80086b60834b089718f03cda5" + integrity sha512-hQLD5NURHmzB4Symo6JJ5HDw2TWwhr6T3gw9aChNMsZvkxcD8U8Gcz/hllAOOMGE+HO3ScpRPahpXDQRgF19JQ== + dependencies: + hey-listen "^1.0.5" + fresh@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" @@ -4095,6 +4139,11 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" +hey-listen@^1.0.4, hey-listen@^1.0.5: + version "1.0.8" + resolved "https://registry.yarnpkg.com/hey-listen/-/hey-listen-1.0.8.tgz#8e59561ff724908de1aa924ed6ecc84a56a9aa68" + integrity sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q== + hmac-drbg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -6088,11 +6137,47 @@ pkg-dir@^3.0.0: dependencies: find-up "^3.0.0" +popmotion-pose@^3.4.0: + version "3.4.7" + resolved "https://registry.yarnpkg.com/popmotion-pose/-/popmotion-pose-3.4.7.tgz#faa55f5a61f249e7615f536529ed65989f2d031c" + integrity sha512-mOn9fovrAiCRxANGnFyIGSYN9YArhJXbeK9Az5HBSoC3bUbhJ1heYguzboqg/jfrgEoPZ6UdUTgJsyivlYMsPg== + dependencies: + "@popmotion/easing" "^1.0.1" + hey-listen "^1.0.5" + popmotion "^8.6.2" + pose-core "^2.1.0" + style-value-types "^3.0.6" + ts-essentials "^1.0.3" + tslib "^1.9.1" + +popmotion@^8.6.2: + version "8.6.8" + resolved "https://registry.yarnpkg.com/popmotion/-/popmotion-8.6.8.tgz#ccc1eaf1613f0611ba6b48d8171c25a518a1e917" + integrity sha512-BRzdgEHLqicl18RH2+gev/gVAQsU6OJD2eXii+J8Jabvw//fTNdkKIi+o8YM1cFU2QH+bnAAr0PEC3eiXMPJ1w== + dependencies: + "@popmotion/easing" "^1.0.1" + "@popmotion/popcorn" "^0.3.6" + framesync "^4.0.0" + hey-listen "^1.0.5" + style-value-types "^3.1.0" + stylefire "2.4.3" + tslib "^1.9.1" + popper.js@^1.14.1: version "1.14.7" resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.14.7.tgz#e31ec06cfac6a97a53280c3e55e4e0c860e7738e" integrity sha512-4q1hNvoUre/8srWsH7hnoSJ5xVmIL4qgz+s4qf2TnJIMyZFUFMGH+9vE7mXynAlHSZ/NdTmmow86muD0myUkVQ== +pose-core@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/pose-core/-/pose-core-2.1.0.tgz#653c85a9a06f924611b909b4a2180ce102bbb258" + integrity sha512-36mVAnIgbM6jfyRug8EqqFbazHUAk9dxwVRpX61FlVw3amI/j7AFegtVU56N0Dht2aYDJIhgYPUYraT1CzjHDw== + dependencies: + "@types/invariant" "^2.2.29" + "@types/node" "^10.0.5" + hey-listen "^1.0.5" + tslib "^1.9.1" + 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" @@ -6462,6 +6547,16 @@ react-popper@^0.10.4: popper.js "^1.14.1" prop-types "^15.6.1" +react-pose@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/react-pose/-/react-pose-4.0.8.tgz#91bdfafde60e4096e7878a35dcc77715bed68f24" + integrity sha512-WN/583nKJZkKmKg5ha+eErOGWF9GV6A5EngC7WHQX5b910X9rTlOlxzdKlUy/dDcsTRMZEtHV0Sy2gLPYsVQCQ== + dependencies: + "@emotion/is-prop-valid" "^0.7.3" + hey-listen "^1.0.5" + popmotion-pose "^3.4.0" + tslib "^1.9.1" + react-redux@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.1.1.tgz#88e368682c7fa80e34e055cd7ac56f5936b0f52f" @@ -7456,6 +7551,11 @@ strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= +style-value-types@^3.0.6, style-value-types@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/style-value-types/-/style-value-types-3.1.0.tgz#07e06090e7581adff4c7955d544c1029d0663795" + integrity sha512-7eaMZ8RKWIQUKHPQK7qv3zLYmvZNb2pCmO4WguXdVFymd2Qj9xqSUoo7LQ8Wd8eiLuoSd+uqzsvcodyvD8nn6Q== + styled-jsx@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-3.1.0.tgz#c295e4170298b5bb858f848c4b73e423a73a68f3" @@ -7470,6 +7570,15 @@ styled-jsx@3.1.0: stylis "3.5.3" stylis-rule-sheet "0.0.10" +stylefire@2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/stylefire/-/stylefire-2.4.3.tgz#1b39847251000bda86ebf82fa2bb4be7dafe6bc4" + integrity sha512-8rckFzuDlVWSyrkmnyTg8avadQavk2t6YkFKUUocsXoj/8NScOjb+/avbB4nrmoPtzD0kN7IyuhKq8jimIBTBQ== + dependencies: + framesync "^4.0.0" + hey-listen "^1.0.4" + style-value-types "^3.0.6" + stylis-rule-sheet@0.0.10: version "0.0.10" resolved "https://registry.yarnpkg.com/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz#44e64a2b076643f4b52e5ff71efc04d8c3c4a430" @@ -7676,7 +7785,12 @@ trim-right@^1.0.1: resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= -tslib@^1.9.0: +ts-essentials@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-1.0.4.tgz#ce3b5dade5f5d97cf69889c11bf7d2da8555b15a" + integrity sha512-q3N1xS4vZpRouhYHDPwO0bDW3EZ6SK9CrrDHxi/D6BPReSjpVgWIOpLS2o0gSBZm+7q/wyKp6RVM1AeeW7uyfQ== + +tslib@^1.9.0, tslib@^1.9.1: version "1.9.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==