diff --git a/CHANGELOG.md b/CHANGELOG.md
index bf886494c..cfd01b170 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,10 @@
* Update Airplay icon to modern variant
* Add gradient to background to break up the solid color
* Reformat Admin Settings portion of Admin Panel (previously known as the Updater)
+ * Move home screen, player page controls to bottom of screen on mobile
+ * Update CSS breakpoints to scale the player page better on the smallest of screens
+ * Reformat Player page volume controls to look more modern
+ * Add safeguards in an attempt to reduce volume slider misinputs
## 0.4.5
* Web App
diff --git a/web/src/App.jsx b/web/src/App.jsx
index b5891ef77..2d3a7cd30 100644
--- a/web/src/App.jsx
+++ b/web/src/App.jsx
@@ -222,13 +222,14 @@ Page.propTypes = {
const App = ({ selectedPage }) => {
return (
-
+
-
-
+
{/* Used to make sure the background doesn't stretch or stop prematurely on scrollable pages */}
+
+
-
-
);
};
App.propTypes = {
diff --git a/web/src/App.scss b/web/src/App.scss
index 918bbcb54..38c7b0600 100644
--- a/web/src/App.scss
+++ b/web/src/App.scss
@@ -1,10 +1,16 @@
@use "src/general";
+.background-gradient {
+ background: general.$bg-gradient;
+ position: fixed;
+ z-index: -1;
+ height: 100vh;
+ width: 100vw;
+}
+
.app {
// display: flex;
// width: 100vw;
- height: 100vh; // Required to not have weird paneling with gradient backgrounds
- background: general.$bg-gradient;
// padding-top: 0.6rem;
// padding-bottom: 0.6rem;
// padding: 0.6rem;
@@ -24,3 +30,33 @@
.app-body {
padding-bottom: general.$navbar-height;
}
+
+
+.pill-scrollbar {
+ max-height: inherit;
+ overflow-y: auto;
+}
+
+.pill-scrollbar::-webkit-scrollbar {
+ width: 12px;
+ height: 12px;
+}
+
+.pill-scrollbar::-webkit-scrollbar-track {
+ background: transparent;
+}
+
+.pill-scrollbar::-webkit-scrollbar-thumb {
+ background-color: rgba(0, 0, 0, 0.5);
+ border-radius: 999px;
+ border: 3px solid rgba(255, 255, 255, 0.5);
+}
+
+.pill-scrollbar::-webkit-scrollbar-thumb:hover {
+ background-color: rgba(0, 0, 0, 0.7);
+}
+
+.pill-scrollbar {
+ scrollbar-width: thin;
+ scrollbar-color: rgba(0, 0, 0, 0.5) transparent;
+}
diff --git a/web/src/components/Card/Card.jsx b/web/src/components/Card/Card.jsx
index 86aff4c0d..e74823f24 100644
--- a/web/src/components/Card/Card.jsx
+++ b/web/src/components/Card/Card.jsx
@@ -13,8 +13,10 @@ const Card = ({
selected,
selectable,
onClick,
+
+ secondary,
}) => {
- const cName = `card ${className} ${selectable && !selected ? "selectable" : ""} ${selected ? "selected" : ""}`
+ const cName = `card ${className} ${selectable && !selected ? "selectable" : ""} ${selected ? "selected" : ""} ${secondary ? "secondary" : "primary"}`
const onClickFun = onClick === null ? () => {} : onClick;
const topTransparency = selected ? 0.25 : 0.4;
const bottomTransparency = selected ? 0.25 : 0.9;
@@ -49,6 +51,8 @@ Card.propTypes = {
selected: PropTypes.bool,
selectable: PropTypes.bool,
onClick: PropTypes.func,
+
+ secondary: PropTypes.bool,
};
Card.defaultProps = {
@@ -56,7 +60,9 @@ Card.defaultProps = {
className: "",
selected: false,
selectable: false,
- onClick: null
+ onClick: null,
+
+ secondary: false,
};
export default Card;
diff --git a/web/src/components/Card/Card.scss b/web/src/components/Card/Card.scss
index 4266c568c..899cb90dc 100644
--- a/web/src/components/Card/Card.scss
+++ b/web/src/components/Card/Card.scss
@@ -2,12 +2,18 @@
.card {
@include general.low-shadow;
- background-color: general.$primary;
border-radius: 18px;
color: general.$text-color;
}
+.primary {
+ background-color: general.$primary
+}
+.secondary {
+ background-color: general.$secondary
+}
+
.selected {
@include general.selected-shadow;
}
diff --git a/web/src/components/CustomMarquee/CustomMarquee.jsx b/web/src/components/CustomMarquee/CustomMarquee.jsx
index 72c4befe7..9bcaa22a8 100644
--- a/web/src/components/CustomMarquee/CustomMarquee.jsx
+++ b/web/src/components/CustomMarquee/CustomMarquee.jsx
@@ -42,14 +42,13 @@ export default function CustomMarquee(props) {
}
}
- let resizeTimout;
+ let resizeTimeout; // Your IDE will say this is unused, it's actually used to make sure the timeout below is limited to one instance at a time by taking up a specific variable
function handleResize(){
if(!resizeCooldown.current){
resizeCooldown.current = true;
assessMarquee()
-
- resizeTimout = setTimeout(()=>{resizeCooldown.current = false;}, 1000) // set a cooldown for resize checks to avoid excessive renders
+ resizeTimeout = setTimeout(()=>{resizeCooldown.current = false;}, 1000)
}
}
window.addEventListener("resize", handleResize()); // Doesn't call assessMarquee directly to avoid calling thousands of times per second when resizing window
diff --git a/web/src/components/GroupVolumeSlider/GroupVolumeSlider.scss b/web/src/components/GroupVolumeSlider/GroupVolumeSlider.scss
index f6975cc3d..067767b52 100644
--- a/web/src/components/GroupVolumeSlider/GroupVolumeSlider.scss
+++ b/web/src/components/GroupVolumeSlider/GroupVolumeSlider.scss
@@ -23,5 +23,4 @@
.group-volume-slider {
width: 100%;
- margin: 0 1rem;
}
diff --git a/web/src/components/MediaControl/MediaControl.scss b/web/src/components/MediaControl/MediaControl.scss
index 01e8cbc65..d116fc2c7 100644
--- a/web/src/components/MediaControl/MediaControl.scss
+++ b/web/src/components/MediaControl/MediaControl.scss
@@ -8,7 +8,6 @@
justify-content: center;
margin-top: 1rem;
margin-bottom: 1rem;
- width: 90vw;
}
.media-inner {
@@ -19,6 +18,7 @@
}
.media-control {
+ font-size: 3.5rem;
color: general.$controls-color;
padding-left: 1.5rem;
padding-right: 1.5rem;
@@ -30,11 +30,11 @@
padding-right: 1.25rem;
}
- @media (min-width: 365px) and (max-width: 425px) {
+ @media (min-width: 365px) and (max-width: 435px) {
font-size: 2.5rem;
}
- @media (min-width: 425px) {
+ @media (min-width: 435px) {
font-size: 3.5rem;
}
}
diff --git a/web/src/components/ModalCard/ModalCard.scss b/web/src/components/ModalCard/ModalCard.scss
index f05961579..632bf447b 100644
--- a/web/src/components/ModalCard/ModalCard.scss
+++ b/web/src/components/ModalCard/ModalCard.scss
@@ -47,33 +47,3 @@
.modal-footer-button {
@include general.button-hover;
}
-
-
-.pill-scrollbar {
- max-height: inherit;
- overflow-y: auto;
-}
-
-.pill-scrollbar::-webkit-scrollbar {
- width: 12px;
- height: 12px;
-}
-
-.pill-scrollbar::-webkit-scrollbar-track {
- background: transparent;
-}
-
-.pill-scrollbar::-webkit-scrollbar-thumb {
- background-color: rgba(0, 0, 0, 0.5);
- border-radius: 999px;
- border: 3px solid rgba(255, 255, 255, 0.5);
-}
-
-.pill-scrollbar::-webkit-scrollbar-thumb:hover {
- background-color: rgba(0, 0, 0, 0.7);
-}
-
-.pill-scrollbar {
- scrollbar-width: thin;
- scrollbar-color: rgba(0, 0, 0, 0.5) transparent;
-}
diff --git a/web/src/components/SongInfo/SongInfo.scss b/web/src/components/SongInfo/SongInfo.scss
index ed3b965bf..973f47688 100644
--- a/web/src/components/SongInfo/SongInfo.scss
+++ b/web/src/components/SongInfo/SongInfo.scss
@@ -10,6 +10,11 @@
white-space: nowrap;
overflow: hidden;
+
+ @media (max-width: 435px){
+ max-height: 50vh;
+ max-width: 50vh;
+ }
}
.artist-name {
diff --git a/web/src/components/StreamBar/StreamBar.scss b/web/src/components/StreamBar/StreamBar.scss
index 01c847eaf..d83eebc13 100644
--- a/web/src/components/StreamBar/StreamBar.scss
+++ b/web/src/components/StreamBar/StreamBar.scss
@@ -12,6 +12,7 @@
max-width: 22rem; // The same width as the album art, until the album art starts to shrink at 375px wide
}
@media (max-width: 375px) {
+ max-height: 7.5vh;
max-width: 85vw;
}
}
@@ -21,15 +22,26 @@
color: general.$text-color;
width: 100%;
max-width: 80%; // Leave space for icon
- font-size: 2.5rem;
font-weight: medium;
white-space: nowrap;
overflow: hidden;
@include general.header-font;
padding: 0.5rem;
+ @media (max-height: 500px){
+ font-size: 1.5rem;
+ }
+ @media (min-height: 500px){
+ font-size: 2.5rem;
+ }
}
.stream-bar-icon {
- width: 4rem;
- height: 4rem;
+ @media (max-height: 500px){
+ width: 2rem;
+ height: 2rem;
+ }
+ @media (min-height: 500px){
+ width: 4rem;
+ height: 4rem;
+ }
}
diff --git a/web/src/components/VolumeSlider/VolumeSlider.jsx b/web/src/components/VolumeSlider/VolumeSlider.jsx
index dc9be5ddc..8fa967f32 100644
--- a/web/src/components/VolumeSlider/VolumeSlider.jsx
+++ b/web/src/components/VolumeSlider/VolumeSlider.jsx
@@ -34,6 +34,32 @@ VolIcon.propTypes = {
// generic volume slider used by other volume sliders
const VolumeSlider = ({ vol, mute, setVol, setMute, disabled }) => {
+ const touchStartX = React.useRef(0);
+ const touchStartY = React.useRef(0);
+ const isScrolling = React.useRef(false);
+
+ const handleTouchStart = (e) => {
+ const touch = e.touches[0];
+ touchStartX.current = touch.clientX;
+ touchStartY.current = touch.clientY;
+ isScrolling.current = false;
+ };
+
+ const handleTouchMove = (e) => {
+ const touch = e.touches[0];
+ const diffX = touch.clientX - touchStartX.current;
+ const diffY = touch.clientY - touchStartY.current;
+
+ // Detect vertical drag, allow user to continue dragging within safe boundaries without needing to re-drag the slider
+ isScrolling.current = (Math.abs(diffY) / Math.abs(diffX)) > 1.65; // Equivalent to approximately 60 deg
+ };
+
+ const handleVolChange = (val, force = false) => {
+ if (!isScrolling.current) {
+ setVol(val, force);
+ }
+ }
+
return (
@@ -45,29 +71,31 @@ const VolumeSlider = ({ vol, mute, setVol, setMute, disabled }) => {
>
- {
- if(isIOS() && e.type === "mousedown"){
- return;
- }
- setVol(val);
- }}
- onChangeCommitted={(e, val) => {
- if(isIOS() && e.type === "mouseup"){
- return;
- }
- setVol(val, true);
- }}
- />
-
+