Skip to content

Commit

Permalink
Different layout for mobile and desktop devices
Browse files Browse the repository at this point in the history
  • Loading branch information
stever committed Jun 28, 2023
1 parent 625baae commit d9335de
Show file tree
Hide file tree
Showing 8 changed files with 165 additions and 21 deletions.
2 changes: 1 addition & 1 deletion apps/web/public/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ code {
border-radius: 5px;
font-family: monospace !important;
font-size: 16px !important;
height: calc(100vh - 216px) !important;
height: calc(100vh - 177px) !important;
max-height: 736px !important;
border: 1px solid #5B5B5B;
}
Expand Down
64 changes: 47 additions & 17 deletions apps/web/src/components/HomePage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export default function HomePage() {

const activeIndex = useSelector(state => state?.demo.selectedTabIndex);
const errorItems = useSelector(state => state?.project.errorItems);
const isMobile = useSelector(state => state?.window.isMobile);

const toast = useRef(null);

Expand Down Expand Up @@ -42,24 +43,53 @@ export default function HomePage() {
<Toast ref={toast}/>
<div className="mx-2 my-1">
<div className="grid" style={{width: "100%", padding: 0, margin: 0}}>
<div className="col p-0 mr-2" style={{maxWidth: `calc(100vw - ${width + 41}px`}}>
<TabView
activeIndex={activeIndex}
onTabChange={(e) => dispatch(setSelectedTabIndex(e.index))}>
<TabPanel header="Sinclair BASIC">
<DemoSinclairBasicEditor/>
</TabPanel>
<TabPanel header="Z80 Assembly">
<DemoAssemblyEditor/>
</TabPanel>
</TabView>
</div>
<div className="col-fixed p-0 pt-1" style={{width: `${width}px`}}>
<div style={{height: '53px'}} className="pt-3 pl-1">
{isMobile && (
<>
<div className="col" style={{padding: 0}}>

</div>
<Emulator zoom={zoom} width={width}/>
</div>
</div>
<div className="col-fixed p-0 pt-1" style={{width: `${width}px`}}>
<TabView
activeIndex={activeIndex}
onTabChange={(e) => dispatch(setSelectedTabIndex(e.index))}>
<TabPanel header="Emulator">
<Emulator zoom={zoom} width={width}/>
</TabPanel>
<TabPanel header="Sinclair BASIC">
<DemoSinclairBasicEditor/>
</TabPanel>
<TabPanel header="Z80 Assembly">
<DemoAssemblyEditor/>
</TabPanel>
</TabView>
</div>
<div className="col" style={{padding: 0}}>

</div>
</>
)}
{!isMobile && (
<>
<div className="col p-0 mr-2" style={{maxWidth: `calc(100vw - ${width + 41}px`}}>
<TabView
activeIndex={activeIndex}
onTabChange={(e) => dispatch(setSelectedTabIndex(e.index))}>
<TabPanel header="Sinclair BASIC">
<DemoSinclairBasicEditor/>
</TabPanel>
<TabPanel header="Z80 Assembly">
<DemoAssemblyEditor/>
</TabPanel>
</TabView>
</div>
<div className="col-fixed p-0 pt-1" style={{width: `${width}px`}}>
<div style={{height: '53px'}} className="pt-3 pl-1">

</div>
<Emulator zoom={zoom} width={width}/>
</div>
</>
)}
</div>
</div>
</>
Expand Down
10 changes: 9 additions & 1 deletion apps/web/src/redux/app/sagas.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
receivePrivacyPolicy,
receiveTermsOfUse
} from "./actions";
import {setSelectedTabIndex as setDemoTabIndex} from "../demo/actions";
import {reset} from "../jsspeccy/actions";
import {handleException} from "../../errors";

Expand Down Expand Up @@ -46,6 +47,10 @@ function* handleShowActiveEmulatorActions(_) {
if (isProject) {
history.push(`/projects/${projectId}`);
} else {

// Mobile view has emulator on a tab. Switch to the emulator tab when running code.
const isMobile = yield select((state) => state.window.isMobile);
if (isMobile) yield put(setDemoTabIndex(0));
history.push('/');
}
} catch (e) {
Expand All @@ -57,11 +62,14 @@ function* handleResetEmulatorActions(_) {
try {
const projectId = yield select((state) => state.project.id);
const isProject = typeof projectId !== 'undefined';

if (isProject) {
history.push(`/projects/${projectId}`);
yield put(reset());
} else {

// Mobile view has emulator on a tab. Switch to the emulator tab when running code.
const isMobile = yield select((state) => state.window.isMobile);
if (isMobile) yield put(setDemoTabIndex(0));
history.push('/');
yield put(reset());
}
Expand Down
11 changes: 10 additions & 1 deletion apps/web/src/redux/demo/sagas.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import {takeLatest, select, put, call} from "redux-saga/effects";
import getZmakebasTap from "zmakebas";
import getPasmoTap from "pasmo";
import {actionTypes} from "./actions";
import {actionTypes, setSelectedTabIndex} from "./actions";
import {loadTap, pause} from "../jsspeccy/actions";
import {setErrorItems} from "../project/actions";
import {handleException} from "../../errors";
import {dashboardUnlock} from "../../dashboard_lock";

// -----------------------------------------------------------------------------
Expand Down Expand Up @@ -38,6 +39,10 @@ function* handleRunAssemblyActions(_) {
const code = yield select((state) => state.demo.asmCode);
const tap = yield call(getPasmoTap, code);
yield put(loadTap(tap));

// Mobile view has emulator on a tab. Switch to the emulator tab when running code.
const isMobile = yield select((state) => state.window.isMobile);
if (isMobile) yield put(setSelectedTabIndex(0));
} catch (e) {
yield put(setErrorItems(e));
} finally {
Expand All @@ -50,6 +55,10 @@ function* handleRunSinclairBasicActions(_) {
const code = yield select((state) => state.demo.sinclairBasicCode);
const tap = yield call(getZmakebasTap, code);
yield put(loadTap(tap));

// Mobile view has emulator on a tab. Switch to the emulator tab when running code.
const isMobile = yield select((state) => state.window.isMobile);
if (isMobile) yield put(setSelectedTabIndex(0));
} catch (e) {
yield put(setErrorItems(e));
} finally {
Expand Down
6 changes: 5 additions & 1 deletion apps/web/src/redux/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import identityReducer from "./identity/reducers";
import projectReducer from "./project/reducers";
import projectListReducer from "./projectList/reducers";
import subscriberReducer from "./subscriber/reducers";
import windowReducer from "./window/reducers";

// Sagas
import * as appSagas from "./app/sagas";
Expand All @@ -24,6 +25,7 @@ import * as jsspeccySagas from "./jsspeccy/sagas";
import * as projectSagas from "./project/sagas";
import * as projectListSagas from "./projectList/sagas";
import * as subscriberSagas from "./subscriber/sagas";
import * as windowSagas from "./window/sagas";

const loggingMiddleware = (store) => {
return (next) => {
Expand Down Expand Up @@ -63,7 +65,8 @@ const rootReducer = combineReducers({
identity: identityReducer,
project: projectReducer,
projectList: projectListReducer,
subscriber:subscriberReducer,
subscriber: subscriberReducer,
window: windowReducer,
});

export const store = createStore(
Expand Down Expand Up @@ -91,6 +94,7 @@ collectSagas(jsspeccySagas);
collectSagas(projectSagas);
collectSagas(projectListSagas);
collectSagas(subscriberSagas);
collectSagas(windowSagas);

function* rootSaga() {
yield all(sagas);
Expand Down
9 changes: 9 additions & 0 deletions apps/web/src/redux/window/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const actionTypes = {
resized: 'window/resized',
};

export const resized = (width, height) => ({
type: actionTypes.resized,
width,
height,
});
39 changes: 39 additions & 0 deletions apps/web/src/redux/window/reducers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { actionTypes } from './actions';

const mobileMaxWidth = 768;

// -----------------------------------------------------------------------------
// Initial state
// -----------------------------------------------------------------------------

const initialState = {
width: window.innerWidth,
height: window.innerHeight,
isMobile: window.innerWidth <= mobileMaxWidth,
};

// -----------------------------------------------------------------------------
// Actions
// -----------------------------------------------------------------------------

function resized(state, action) {
return {
...state,
width: action.width,
height: action.height,
isMobile: action.width <= mobileMaxWidth,
};
}

// -----------------------------------------------------------------------------
// Reducer
// -----------------------------------------------------------------------------

const actionsMap = {
[actionTypes.resized]: resized,
};

export default function reducer(state = initialState, action) {
const reducerFunction = actionsMap[action.type];
return reducerFunction ? reducerFunction(state, action) : state;
}
45 changes: 45 additions & 0 deletions apps/web/src/redux/window/sagas.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { take, put, call } from 'redux-saga/effects';
import { eventChannel } from 'redux-saga';
import { resized } from './actions';

// -----------------------------------------------------------------------------
// Action watchers
// -----------------------------------------------------------------------------

// noinspection JSUnusedGlobalSymbols
export function* watchForWindowResizeEvents() {
const chan = yield call(getWindowResizeEventChannel);
try {
let i = 0; // NOTE: This is a workaround for SonarQube not accepting an infinite loop here!
while (i < 1000) {
const area = yield take(chan);
yield put(resized(area.width, area.height));
i++;
i--;
}
} finally {
chan.close();
}
}

// -----------------------------------------------------------------------------
// Action handlers
// -----------------------------------------------------------------------------

function getWindowResizeEventChannel() {
return eventChannel((emit) => {
const emitter = () => {
emit({
width: window.innerWidth,
height: window.innerHeight,
});
};

window.addEventListener('resize', emitter);

return () => {
// Must return an unsubscribe function.
window.removeEventListener('resize', emitter);
};
});
}

0 comments on commit d9335de

Please sign in to comment.