Skip to content

Commit

Permalink
Merge pull request #21 from keep-starknet-strange/feat/responsive-and…
Browse files Browse the repository at this point in the history
…-templates

feat: Responsive frontend and Template image initial setup
  • Loading branch information
b-j-roberts authored Apr 10, 2024
2 parents 2b92db7 + 1c1a2dc commit 4706db6
Show file tree
Hide file tree
Showing 24 changed files with 504 additions and 115 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ However, it might be worth running only certain components for development/testi

- [Diagrams](./docs/diagrams/)
- [r/place technical document](https://www.redditinc.com/blog/how-we-built-rplace)
- [Telegram](https://t.me/+rd1pvEo8T2w2ZDRh)
- [OnlyDust](https://app.onlydust.com/p/artpeace)

## Contributors ✨

Expand Down
41 changes: 41 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"get-starknet": "^3.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-responsive": "^10.0.0",
"react-scripts": "5.0.1",
"react-use-websocket": "^4.8.1",
"starknet": "^5.24.3"
Expand Down
19 changes: 18 additions & 1 deletion frontend/src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
z-index: 100;
right: 0;
top: 0;
width: max(22rem, 25%);
width: max(25rem, 30%);
margin: 0.5rem;
padding: 0;

Expand All @@ -26,6 +26,14 @@
font-size: 1.2rem;
}

.App__panel--tablet {
width: max(28rem, 40%);
}

.App__panel--portrait {
width: max(28rem, 60%);
}

.App__footer {
position: fixed;
z-index: 101;
Expand All @@ -40,3 +48,12 @@

pointer-events: none;
}

.App__logo--mobile {
position: fixed;
z-index: 99;
left: 1rem;
top: 1rem;
width: min(8rem, 15%);
image-rendering: pixelated;
}
36 changes: 31 additions & 5 deletions frontend/src/App.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,38 @@
import React, { useState } from 'react';
import { useMediaQuery } from 'react-responsive'
import './App.css';
import Canvas from './canvas/Canvas.js';
import PixelSelector from './canvas/PixelSelector.js';
import SelectedPixelPanel from './canvas/SelectedPixelPanel.js';
import TabsFooter from './tabs/TabsFooter.js';
import TabPanel from './tabs/TabPanel.js';
import { usePreventZoom } from './utils/Window.js';
import logo from './resources/logo.png';

function App() {
// Window management
usePreventZoom();

const isDesktopOrLaptop = useMediaQuery({
query: '(min-width: 1224px)'
})
const isBigScreen = useMediaQuery({ query: '(min-width: 1824px)' })
const isTabletOrMobile = useMediaQuery({ query: '(max-width: 1224px)' })
const isPortrait = useMediaQuery({ query: '(orientation: portrait)' })
// TODO: Consider using in sizing stuff
const isRetina = useMediaQuery({ query: '(min-resolution: 2dppx)' })
// TODO: height checks ?

const getDeviceTypeInfo = () => {
return {
isDesktopOrLaptop: isDesktopOrLaptop,
isBigScreen: isBigScreen,
isTabletOrMobile: isTabletOrMobile,
isPortrait: isPortrait,
isRetina: isRetina
}
}

// Pixel selection data
const [selectedColorId, setSelectedColorId] = useState(-1);
const [pixelSelectedMode, setPixelSelectedMode] = useState(false);
Expand All @@ -35,15 +58,18 @@ function App() {
return (
<div className="App">
<Canvas selectedColorId={selectedColorId} setSelectedColorId={setSelectedColorId} pixelSelectedMode={pixelSelectedMode} selectedPositionX={selectedPositionX} selectedPositionY={selectedPositionY} setPixelSelection={setPixelSelection} clearPixelSelection={clearPixelSelection} />
<div className="App__panel">
{ pixelSelectedMode && (
{ !isDesktopOrLaptop && (
<img src={logo} alt="logo" className="App__logo--mobile" />
)}
<div className={"App__panel " + (isTabletOrMobile ? "App__panel--tablet " : " ") + (isPortrait ? "App__panel--portrait " : " ")}>
{ (!isPortrait ? pixelSelectedMode : pixelSelectedMode && activeTab === tabs[0]) && (
<SelectedPixelPanel selectedPositionX={selectedPositionX} selectedPositionY={selectedPositionY} clearPixelSelection={clearPixelSelection} />
)}
<TabPanel activeTab={activeTab} setActiveTab={setActiveTab} />
<TabPanel activeTab={activeTab} setActiveTab={setActiveTab} getDeviceTypeInfo={getDeviceTypeInfo} />
</div>
<div className="App__footer">
<PixelSelector selectedColorId={selectedColorId} setSelectedColorId={setSelectedColorId} />
<TabsFooter tabs={tabs} activeTab={activeTab} setActiveTab={setActiveTab} />
<PixelSelector selectedColorId={selectedColorId} setSelectedColorId={setSelectedColorId} getDeviceTypeInfo={getDeviceTypeInfo} />
<TabsFooter tabs={tabs} activeTab={activeTab} setActiveTab={setActiveTab} getDeviceTypeInfo={getDeviceTypeInfo} />
</div>
</div>
);
Expand Down
17 changes: 7 additions & 10 deletions frontend/src/canvas/Canvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const Canvas = props => {
setDragStartY(e.clientY)
}

const handlePointerUp = (e) => {
const handlePointerUp = () => {
setIsDragging(false)
setDragStartX(0)
setDragStartY(0)
Expand All @@ -90,12 +90,12 @@ const Canvas = props => {
const [setup, setSetup] = useState(false)
const [pixelPlacedBy, setPixelPlacedBy] = useState("")

const draw = (ctx, imageData) => {
const draw = useCallback((ctx, imageData) => {
ctx.canvas.width = width
ctx.canvas.height = height
ctx.putImageData(imageData, 0, 0)
// TODO: Use image-rendering for supported browsers?
}
}, [width, height])

useEffect(() => {
if (setup) {
Expand All @@ -122,7 +122,7 @@ const Canvas = props => {
let value = (byte >> (oneByteBitOffset - bitOffset)) & 0b11111
dataArray.push(value)
} else {
let byte = colorData[bytePos] << 8 | colorData[bytePos + 1]
let byte = (colorData[bytePos] << 8) | colorData[bytePos + 1]
let value = (byte >> (twoByteBitOffset - bitOffset)) & 0b11111
dataArray.push(value)
}
Expand Down Expand Up @@ -152,7 +152,7 @@ const Canvas = props => {
})
}
// TODO: Return a cleanup function to close the websocket / ...
}, [draw, readyState])
}, [readyState, sendJsonMessage, setup, colors, width, height, backendUrl, draw])

useEffect(() => {
if (lastJsonMessage) {
Expand All @@ -166,7 +166,7 @@ const Canvas = props => {
context.fillStyle = color
context.fillRect(x, y, 1, 1)
}
}, [lastJsonMessage])
}, [lastJsonMessage, colors, width])

const pixelSelect = useCallback((clientX, clientY) => {
const canvas = canvasRef.current
Expand All @@ -189,17 +189,14 @@ const Canvas = props => {
}).then(response => {
return response.text()
}).then(data => {
// TODO: not working
// TODO: Cache pixel info & clear cache on update from websocket
// TODO: Dont query if hover select ( until 1s after hover? )
setPixelPlacedBy(data)
}).catch(error => {
console.error(error)
//TODO: Handle error
});

// TODO: Create a border around the selected pixel
}, [props.setSelectedPositionX, props.setSelectedPositionY, props.setPixelSelectedMode, setPixelPlacedBy, width, height, props.selectedColorId, props.pixelSelectedMode, props.selectedPositionX, props.selectedPositionY])
}, [props, width, height, backendUrl])

const pixelClicked = (e) => {
pixelSelect(e.clientX, e.clientY)
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/canvas/PixelSelector.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
pointer-events: fill;
}

.PixelSelector--portrait {
font-size: min(1.4rem, 3vw);
margin: 1rem;
}

.PixelSelector__button {
padding: 0 1rem;
border-radius: 1rem;
Expand All @@ -14,6 +19,7 @@
align-items: center;
justify-content: center;

/* TODO: style this better? */
background-image: linear-gradient(to bottom right, rgba(50, 50, 50, 0.25), rgba(50, 50, 50, 0.5));
box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.1);

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/canvas/PixelSelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ const PixelSelector = (props) => {
}, [getTimeTillNextPlacement]);

return (
<div className="PixelSelector">
<div className={"PixelSelector " + (props.getDeviceTypeInfo().isPortrait ? " PixelSelector--portrait" : "")}>
{
selectorMode &&
<div className="PixelSelector__selector">
Expand Down
2 changes: 0 additions & 2 deletions frontend/src/canvas/TemplateOverlay.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ const TemplateOverlay = props => {
const width = props.templateWidth
const height = templateImage.length / width
const scaler = 8
const scalerFactor = 1 / scaler
const translater = (scaler - 1) / 2

const draw = (ctx, imageData) => {
ctx.canvas.width = width * scaler
Expand Down
1 change: 1 addition & 0 deletions frontend/src/tabs/ExpandableTab.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useState } from 'react';
import './ExpandableTab.css';

const ExpandableTab = props => {
// TODO: Close pixel selection when expanded
const [expanded, setExpanded] = useState(false);

const MainSection = props.mainSection;
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/tabs/NFTs.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from 'react'
import React from 'react'
import './NFTs.css';
import ExpandableTab from './ExpandableTab.js';

Expand Down
15 changes: 13 additions & 2 deletions frontend/src/tabs/TabsFooter.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

/* TODO: css -> scss? */
.TabsFooter__logo {
width: min(8rem, 10vw);
width: 10rem;

pointer-events: fill;
image-rendering: pixelated;
Expand All @@ -25,9 +25,20 @@
}

.TabsFooter__tab {
font-size: min(1.6rem, 2vw);
font-size: min(1.6rem, 1.6vw);
text-shadow: 0 0 1rem #FFFFFF;
pointer-events: all;
padding: 2rem 0;
}

.TabsFooter__tab--portrait {
font-size: min(2rem, 2.2vw);
text-shadow: 0 0 1rem #FFFFFF;
pointer-events: all;
padding: 1.6rem 0.4rem;
border-radius: 0.5rem;
background: linear-gradient(to bottom right, rgba(255, 255, 255, 0.4), rgba(255, 255, 255, 0.6));
box-shadow: 0 0 1rem 0.1rem rgba(0, 0, 0, 0.2);
}

.TabsFooter__main {
Expand Down
8 changes: 5 additions & 3 deletions frontend/src/tabs/TabsFooter.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@ const TabsFooter = props => {
return (
<div className="TabsFooter">
<div key={props.tabs[0]} onClick={() => props.setActiveTab(props.tabs[0])} className="TabsFooter__main">
<img src={logo} alt="logo" className="Tabs__logo" />
<div className={"TabsFooter__tab " + (props.activeTab === props.tabs[0] ? 'TabsFooter__tab--active' : '')}>Canvas</div>
{ props.getDeviceTypeInfo().isDesktopOrLaptop &&
<img src={logo} alt="logo" className="TabsFooter__logo" />
}
<div className={"TabsFooter__tab " + (props.activeTab === props.tabs[0] ? 'TabsFooter__tab--active ' : ' ') + (props.getDeviceTypeInfo().isPortrait ? 'TabsFooter__tab--portrait' : ' ')}>Canvas</div>
</div>

{props.tabs.slice(1, props.tabs.length).map((type) => (
<div
key={type}
onClick={() => props.setActiveTab(type)}
className={"TabsFooter__tab " + (props.activeTab === type ? 'TabsFooter__tab--active' : '')}
className={"TabsFooter__tab " + (props.activeTab === type ? 'TabsFooter__tab--active ' : ' ') + (props.getDeviceTypeInfo().isPortrait ? 'TabsFooter__tab--portrait' : '')}
>
{type}
</div>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/tabs/Templates.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from 'react'
import React from 'react'
import './Templates.css';
import ExpandableTab from './ExpandableTab.js';

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/tabs/Voting.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const Voting = props => {
<div style={{gridArea: "color"}}>Color</div>
<div style={{gridArea: "votes"}}>Count</div>
</div>
<div style={{height: '55vh', overflow: 'scroll'}}>
<div style={{height: '52vh', overflow: 'scroll'}}>
{colors.map((color, index) => (
<div key={index} className="Voting__colors__item">
<div className="Voting__colors__item__vote" onClick={() => {
Expand Down
Loading

0 comments on commit 4706db6

Please sign in to comment.