Skip to content

Commit 26f13de

Browse files
committed
Add ability to share encoded roster via URL
1 parent 60d9e3e commit 26f13de

5 files changed

Lines changed: 62 additions & 27 deletions

File tree

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ export default defineConfig([
8989
- [ ] The ability to store these lineups somewhere so we can retrieve them
9090
- [x] Local Storage
9191
- [ ] Database
92-
- [ ] The ability to share lineups with others
93-
- [ ] Encoded url
94-
- [ ] Save code
92+
- [x] The ability to share lineups with others
93+
- [x] Encoded url
94+
- [ ] Generate a tailored pre-game script based on the roles in the roster
95+
- [ ] Play audio as a text-to-speech for that script

src/App.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ import { RoleCard } from './components/role-card'
55
import { Roster } from './components/roster'
66
import { RosterRepository } from './components/roster-respository'
77
import { generateRoster } from './lib/generate-roster'
8+
import {
9+
encodeCommaSeparatedRosterToBase64,
10+
getRosterFromQueryString,
11+
} from './lib/role-utils'
812
import { type Role, Roles } from './model/roles'
913
import { DefaultRosters } from './model/rosters'
10-
import { getRosterFromQueryString } from './lib/role-utils'
1114

1215
function App() {
1316
const roles = Object.values(Roles) //.filter((role) => role.balance === 0)
@@ -253,7 +256,15 @@ function App() {
253256
function setRoster(draftedRoles: string[]) {
254257
// Get the current URL
255258
const currentUrl = new URL(window.location.href)
256-
currentUrl.searchParams.set('roster', 'TEST') // Adds 'paramName=paramValue' or updates its value if it already exists
259+
260+
const base64Roster = encodeCommaSeparatedRosterToBase64(
261+
draftedRoles.map((cardId) => {
262+
const [_, role] = cardId.split('|')
263+
return role
264+
})
265+
)
266+
267+
currentUrl.searchParams.set('roster', base64Roster) // Adds 'paramName=paramValue' or updates its value if it already exists
257268

258269
const newUrl = currentUrl.toString()
259270
//Update query params

src/components/roster-respository.tsx

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1+
import { copyRosterToClipboard } from '../lib/role-utils'
12
import { Roles } from '../model/roles'
2-
import type { Lineup } from '../model/rosters'
33

44
interface RosterRepositoryProps {
55
activePreset: string
66
customRosterName: string
77
deleteRoster: () => void
8-
rosterLineups: Record<string, Lineup>
8+
rosterLineups: Record<string, string[]>
99
saveRoster: () => void
1010
selectRoster: (selectedRoster: string) => void
1111
setCustomRosterName: React.Dispatch<React.SetStateAction<string>>
@@ -20,11 +20,11 @@ export function RosterRepository({
2020
saveRoster,
2121
selectRoster,
2222
setCustomRosterName,
23-
generateRoster
23+
generateRoster,
2424
}: RosterRepositoryProps) {
2525
return (
2626
<div className="flex flex-wrap gap-1 justify-center content-baseline">
27-
<div id="load-roster" className='md:flex-1 flex-nowrap text-nowrap'>
27+
<div id="load-roster" className="md:flex-1 flex-nowrap text-nowrap">
2828
<div id="select-roster">
2929
<span className="font-semibold">Preset:</span>
3030
<select
@@ -52,7 +52,7 @@ export function RosterRepository({
5252
</span>
5353
</div>
5454
</div>
55-
<div className='md:flex-1 flex-nowrap text-nowrap'>
55+
<div className="md:flex-1 flex-nowrap text-nowrap">
5656
<input
5757
value={customRosterName}
5858
onChange={(e) => setCustomRosterName(e.target.value)}
@@ -63,25 +63,33 @@ export function RosterRepository({
6363
className="p-1.5 pl-3 rounded-l-xl placeholder:text-gray-100 placeholder:italic bg-slate-900 text-slate-200"
6464
/>
6565
<span
66-
onClick={_ => customRosterName && saveRoster()}
66+
onClick={(_) => customRosterName && saveRoster()}
6767
className={`${!customRosterName ? 'disabled text-gray-800 bg-gray-600' : 'bg-purple-600 hover:bg-purple-500 active:bg-purple-800'} select-none font-semibold rounded-r-lg p-2 `}
6868
>
6969
Save Current Roster
7070
</span>
7171
</div>
7272

7373
<div className="bg-indigo-900 shadow-2xl rounded-full p-3">
74-
<span className='font-semibold select-none'>Generate Lineup:</span>{' '}
74+
<span className="font-semibold select-none">Generate Lineup:</span>{' '}
7575
{[4, 5, 6, 7, 8, 9, 10, 11].map((numPlayers) => (
7676
<span
7777
key={numPlayers}
78-
className={`mx-1 px-3 py-2 select-none ${numPlayers % 2 == 0 ? 'bg-teal-700 hover:bg-teal-600 active:bg-teal-800': 'bg-amber-700 hover:bg-amber-600 active:bg-amber-800'} rounded-full cursor-pointer`}
78+
className={`mx-1 px-3 py-2 select-none ${numPlayers % 2 == 0 ? 'bg-teal-700 hover:bg-teal-600 active:bg-teal-800' : 'bg-amber-700 hover:bg-amber-600 active:bg-amber-800'} rounded-full cursor-pointer`}
7979
onClick={() => generateRoster(numPlayers)}
8080
>
8181
{numPlayers}
8282
</span>
8383
))}
8484
</div>
85+
86+
{/* Share Button */}
87+
<span
88+
onClick={copyRosterToClipboard}
89+
className="select-none text-nowrap font-semibold flex-nowrap bg-indigo-600 hover:bg-indigo-500 active:bg-indigo-800 rounded-full px-3 py-3"
90+
>
91+
Share Roster 🔗
92+
</span>
8593
</div>
8694
)
8795
}

src/components/roster.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useDroppable } from '@dnd-kit/core'
2+
import { useEffect, useState } from 'react'
23
import { Roles, type Role } from '../model/roles'
34
import { RoleCard } from './role-card'
4-
import { useEffect, useState } from 'react'
55

66
interface RosterProps {
77
children: any
@@ -51,7 +51,9 @@ export function Roster(props: RosterProps) {
5151

5252
return (
5353
<div>
54-
<h2 className="text-2xl font-bold">Roster{rosterIsModified && '*'}</h2>
54+
<div className='flex justify-center gap-3 items-center m-1'>
55+
<span className="text-2xl font-bold">Roster{rosterIsModified && '*'}</span>
56+
</div>
5557
<div className="select-none rounded-2xl bg-indigo-950 border-dashed border-slate-400 border-2 min-w-30 min-h-30 mb-2 p-2">
5658
<div className="flex flex-wrap gap-3 mb-3">
5759
<span className="grow text-xl font-bold">

src/lib/role-utils.ts

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,28 @@ import { DefaultRosters } from '../model/rosters'
44
export function convertRosterToCommaSeparatedIds(roster: Role[]): string {
55
return roster.map((role) => role.id).join(',')
66
}
7-
export function copyRosterToClipboard(roster: Role[]) {
8-
navigator.clipboard.writeText(convertRosterToCommaSeparatedIds(roster))
7+
8+
export function copyRosterToClipboard() {
9+
navigator.clipboard.writeText(window.location.href)
10+
alert('Copied roster url to clipboard!')
911
}
1012

1113
export function convertRosterToBase64(roster: Role[]): string {
1214
return btoa(convertRosterToCommaSeparatedIds(roster))
1315
}
1416

15-
export function convertCommaSeparatedRolesToArray(rosterString: string): Role[] {
16-
return rosterString.split(',').map(id => {
17+
export function encodeCommaSeparatedRosterToBase64(
18+
rosterIds: string[]
19+
): string {
20+
return btoa(rosterIds.join(','))
21+
}
22+
23+
export function convertCommaSeparatedRolesToArray(
24+
rosterString: string
25+
): Role[] {
26+
return rosterString.split(',').map((id) => {
1727
if (Object.keys(Roles).includes(id)) {
18-
return Roles[id]
28+
return Roles[id]
1929
}
2030

2131
const unknownRole: Role = Roles['UNKNOWN']
@@ -25,16 +35,19 @@ export function convertCommaSeparatedRolesToArray(rosterString: string): Role[]
2535

2636
export function decodeEncodedRoster(base64String: string): Role[] {
2737
const commaSeparatedRoster = atob(base64String)
38+
console.log('Roster from Base64String:', commaSeparatedRoster)
2839
return convertCommaSeparatedRolesToArray(commaSeparatedRoster)
2940
}
3041

3142
export function getRosterFromQueryString(): Role[] {
32-
const urlParams = new URLSearchParams(window.location.search)
43+
const urlParams = new URLSearchParams(window.location.search)
3344

34-
const encodedRosterParam = urlParams.get('roster')
35-
if (encodedRosterParam) {
36-
return decodeEncodedRoster(encodedRosterParam)
37-
}
45+
const encodedRosterParam = urlParams.get('roster')
46+
if (encodedRosterParam) {
47+
return decodeEncodedRoster(encodedRosterParam)
48+
}
3849

39-
return convertCommaSeparatedRolesToArray(DefaultRosters['Blank Slate'].join(','))
40-
}
50+
return convertCommaSeparatedRolesToArray(
51+
DefaultRosters['Blank Slate'].join(',')
52+
)
53+
}

0 commit comments

Comments
 (0)