Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix auto-resize for Validate and Explore page #3649

Merged
merged 31 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
260d555
Auto resize for explore page
jsomeara Sep 9, 2024
5d058c6
Merge branch 'develop' into 3626-auto-resize-explore-validate
jsomeara Sep 9, 2024
66e0ef7
Merge branch 'develop' into 3626-auto-resize-explore-validate
jsomeara Sep 13, 2024
6d4e67e
Apply height awareness for new beta auto resize.
jsomeara Sep 13, 2024
edbf82f
Fill up whole space, even when zoomed out
jsomeara Sep 13, 2024
bc8aa6d
Mimick normal zoom on explorer iframe
jsomeara Sep 13, 2024
d2b3858
auto resize: Passthrough iframe url changes to parent document
jsomeara Sep 14, 2024
f84340e
Merge branch 'develop' into 3626-auto-resize-explore-validate
jsomeara Sep 14, 2024
9486eb7
Passthrough svl and InitialMissionInstruction
jsomeara Sep 22, 2024
708e8fc
Remove Visit_Explore_Wrapper logging
jsomeara Sep 22, 2024
15ebe37
Move inline css to svl.css file
jsomeara Sep 22, 2024
a392b4b
Switch element id from wrapperFrame to wrapper-frame
jsomeara Sep 22, 2024
beeda3f
Add bottom newlines to files
jsomeara Sep 22, 2024
fbb991f
Add comments to wrapperFrame.js
jsomeara Sep 22, 2024
b2a7dc5
Add to validate page
jsomeara Sep 22, 2024
c20b45a
Merge branch 'develop' into 3626-auto-resize-explore-validate
jsomeara Sep 22, 2024
5b8db71
add auto resize to new validate beta
jsomeara Sep 22, 2024
a2e6b68
Merge branch '3626-auto-resize-explore-validate' of https://github.co…
jsomeara Sep 22, 2024
20dde33
Merge branch 'develop' of https://github.com/ProjectSidewalk/Sidewalk…
misaugstad Sep 30, 2024
782d6a2
reduces bottom padding a bit for auto zoom
misaugstad Sep 30, 2024
0fedac6
Added ability to specify percentage of vertical whitespace
jsomeara Nov 23, 2024
659c80b
Merge branch 'develop' into 3626-auto-resize-explore-validate
jsomeara Nov 23, 2024
56c56bc
Make SVL padding top 0
jsomeara Nov 23, 2024
107560d
small code cleanup for automated zoom with iframes
misaugstad Nov 26, 2024
fd77ca1
fix inconsistent padding + minor fixes
jsomeara Nov 26, 2024
7aff3cd
Improved padding on newValidateBeta
jsomeara Nov 26, 2024
88373cc
Increase width of tool-ui in newValidateBeta
jsomeara Nov 26, 2024
86f48ce
updates Validate page mission header
misaugstad Dec 2, 2024
484baf1
small UI padding adjustments
misaugstad Dec 2, 2024
72ec63f
adds automatic zoom to AdminValidate
misaugstad Dec 2, 2024
b821ae2
final cleanup for auto resizing explore/validate
misaugstad Dec 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
441 changes: 234 additions & 207 deletions app/controllers/AuditController.scala

Large diffs are not rendered by default.

76 changes: 57 additions & 19 deletions app/controllers/ValidationController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,41 +38,79 @@ class ValidationController @Inject() (implicit val env: Environment[User, Sessio
val validationMissionStr: String = "validation"

/**
* Returns the validation page.
* Returns /validate wrapper page.
*/
def validate = UserAwareAction.async { implicit request =>
def validateWrapper = UserAwareAction.async { implicit request =>
val timestamp: Timestamp = new Timestamp(Instant.now.toEpochMilli)
val ipAddress: String = request.remoteAddress

request.identity match {
case Some(user) =>
val adminParams = AdminValidateParams(adminVersion = false)
val r: UserAwareRequest[AnyContent] = request
val validationData = getDataForValidationPages(request, labelCount = 10, "Visit_Validate", adminParams)
if (validationData._4.missionType != "validation") {
Future.successful(Redirect("/explore"))
} else {
Future.successful(Ok(views.html.validation("Sidewalk - Validate", Some(user), adminParams, validationData._1, validationData._2, validationData._3, validationData._4.numComplete, validationData._5, validationData._6, validationData._7)))
}
Future.successful(Ok(views.html.validationWrapper("Sidewalk - Validate", Some(user))))
case None =>
Future.successful(Redirect(s"/anonSignUp?url=/validate"));
Future.successful(Redirect("/anonSignUp?url=/validate"))
}
}

/**
* Returns the new validation that includes severity and tags page.
*/
def newValidateBeta = UserAwareAction.async { implicit request =>
if (isAdmin(request.identity)) {
* Returns the validation page.
*/
def validate = UserAwareAction.async { implicit request =>
if (request.headers.get("Sec-Fetch-Dest").getOrElse("") == "document") {
validateWrapper.apply(request)
} else {
request.identity match {
case Some(user) =>
val adminParams = AdminValidateParams(adminVersion = false)
val validationData = getDataForValidationPages(request, labelCount = 10, "Visit_NewValidateBeta", adminParams)
val r: UserAwareRequest[AnyContent] = request
val validationData = getDataForValidationPages(request, labelCount = 10, "Visit_Validate", adminParams)
if (validationData._4.missionType != "validation") {
Future.successful(Redirect("/explore"))
} else {
val tags: List[Tag] = TagTable.getTagsForCurrentCity
Future.successful(Ok(views.html.newValidateBeta("Sidewalk - NewValidateBeta", Some(user), adminParams, validationData._1, validationData._2, validationData._3, validationData._4.numComplete, validationData._5, validationData._6, tags)))
Future.successful(Ok(views.html.validation("Sidewalk - Validate", Some(user), adminParams, validationData._1, validationData._2, validationData._3, validationData._4.numComplete, validationData._5, validationData._6, validationData._7)))
}
case None =>
Future.successful(Redirect(s"/anonSignUp?url=/newValidateBeta"));
Future.successful(Redirect(s"/anonSignUp?url=/validate"));
}
}
}

/**
* Returns /newValidateBeta wrapper page.
*/
def newValidateBetaWrapper = UserAwareAction.async { implicit request =>
val timestamp: Timestamp = new Timestamp(Instant.now.toEpochMilli)
val ipAddress: String = request.remoteAddress

request.identity match {
case Some(user) =>
Future.successful(Ok(views.html.newValidateBetaWrapper("Sidewalk - NewValidateBeta", Some(user))))
case None =>
Future.successful(Redirect("/anonSignUp?url=/newValidateBeta"))
}
}

/**
* Returns the new validation that includes severity and tags page.
*/
def newValidateBeta = UserAwareAction.async { implicit request =>
if (isAdmin(request.identity)) {
if (request.headers.get("Sec-Fetch-Dest").getOrElse("") == "document") {
newValidateBetaWrapper.apply(request)
} else {
request.identity match {
case Some(user) =>
val adminParams = AdminValidateParams(adminVersion = false)
val validationData = getDataForValidationPages(request, labelCount = 10, "Visit_NewValidateBeta", adminParams)
if (validationData._4.missionType != "validation") {
Future.successful(Redirect("/explore"))
} else {
val tags: List[Tag] = TagTable.getTagsForCurrentCity
Future.successful(Ok(views.html.newValidateBeta("Sidewalk - NewValidateBeta", Some(user), adminParams, validationData._1, validationData._2, validationData._3, validationData._4.numComplete, validationData._5, validationData._6, tags)))
}
case None =>
Future.successful(Redirect(s"/anonSignUp?url=/newValidateBeta"));
}
}
} else {
Future.failed(new AuthenticationException("This is a beta currently only open to Admins."))
Expand Down
6 changes: 0 additions & 6 deletions app/views/explore.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,6 @@
@currentCity = @{cityInfo.filter(c => c.current).head}

@main(title, Some("/explore")) {
@if(userRoute.isDefined) {
@navbar(user, Some(s"/explore?routeId=${userRoute.get.routeId}&resumeRoute=true"))
} else {
@navbar(user, Some("/explore"))
}

<link rel="stylesheet" href='@routes.Assets.at("javascripts/SVLabel/build/SVLabel.css")'/>
<link rel="stylesheet" href='@routes.Assets.at("stylesheets/animate.css")'/>

Expand Down
11 changes: 11 additions & 0 deletions app/views/exploreWrapper.scala.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@import models.user.User
@(title: String, user: Option[User] = None)(implicit lang: Lang)

@main(title, Some("/explore")) {
@navbar(user, Some("/explore"))
<link rel="stylesheet" href='@routes.Assets.at("stylesheets/wrapperFrame.css")'/>

<iframe id="wrapper-frame"></iframe>

<script type="text/javascript" src='@routes.Assets.at("javascripts/common/wrapperFrame.js")'></script>
}
2 changes: 0 additions & 2 deletions app/views/newValidateBeta.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
@(title: String, user: Option[User] = None, adminParams: AdminValidateParams, mission: Option[JsValue], labelList: Option[JsValue], progress: Option[JsValue], missionSetProgress: Int, hasNextMission: Boolean, completedValidations: Int, tagList: List[Tag])(implicit lang: Lang)

@main(title, Some("/newValidateBeta")) {
@navbar(user, Some("/newValidateBeta"))

@icons()

<link rel="stylesheet" href='@routes.Assets.at("stylesheets/animate.css")'/>
Expand Down
11 changes: 11 additions & 0 deletions app/views/newValidateBetaWrapper.scala.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@import models.user.User
@(title: String, user: Option[User] = None)(implicit lang: Lang)

@main(title, Some("/validate")) {
@navbar(user, Some("/newValidateBeta"))
<link rel="stylesheet" href='@routes.Assets.at("stylesheets/wrapperFrame.css")'/>

<iframe id="wrapper-frame"></iframe>

<script type="text/javascript" src='@routes.Assets.at("javascripts/common/wrapperFrame.js")'></script>
}
2 changes: 0 additions & 2 deletions app/views/validation.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
@currentCity = @{cityInfo.filter(c => c.current).head}

@main(title, Some("/validate")) {
@navbar(user, Some("/validate"))

@icons()

<script type="text/javascript" src='@routes.Assets.at("javascripts/SVValidate/build/SVValidate.js")'></script>
Expand Down
11 changes: 11 additions & 0 deletions app/views/validationWrapper.scala.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@import models.user.User
@(title: String, user: Option[User] = None)(implicit lang: Lang)

@main(title, Some("/validate")) {
@navbar(user, Some("/validate"))
<link rel="stylesheet" href='@routes.Assets.at("stylesheets/wrapperFrame.css")'/>

<iframe id="wrapper-frame"></iframe>

<script type="text/javascript" src='@routes.Assets.at("javascripts/common/wrapperFrame.js")'></script>
}
17 changes: 17 additions & 0 deletions public/javascripts/SVLabel/css/svl.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
html,
body {
overflow: hidden;
}

html {
padding-top: 0px;
}

body {
padding-top: 30px;
}

text {
visibility: hidden;
}
Expand Down Expand Up @@ -209,3 +222,7 @@ input[type="radio"] {
margin-top: 7px;
height: 16px;
}

.test-server-banner {
display: none;
}
11 changes: 6 additions & 5 deletions public/javascripts/SVLabel/src/SVLabel/Main.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ function Main (params) {
google.maps.event.addDomListener(window, 'load', task.render);
}

$("#navbar-retake-tutorial-btn").on('click', function () {
parent.$("#navbar-retake-tutorial-btn").on('click', function () {
window.location.replace('/explore?retakeTutorial=true');
});

Expand Down Expand Up @@ -384,10 +384,11 @@ function Main (params) {

// Use CSS zoom to scale the UI for users with high resolution screens.
// Has only been tested on Chrome and Safari. Firefox doesn't support CSS zoom.
if (bowser.safari) {
svl.cssZoom = util.scaleUI();
window.addEventListener('resize', (e) => { svl.cssZoom = util.scaleUI(); });
}
// 9/8/2024 Update: This has been disabled in favor of an iframe-based scaling approach.
// if (bowser.safari) {
// svl.cssZoom = util.scaleUI();
// window.addEventListener('resize', (e) => { svl.cssZoom = util.scaleUI(); });
// }
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ function Onboarding(svl, audioEffect, compass, form, handAnimation, mapService,

adjustMap();

$("#navbar-retake-tutorial-btn").css("display", "none");
parent.$("#navbar-retake-tutorial-btn").css("display", "none");

var canvasUI = uiOnboarding.canvas.get(0);
if (canvasUI) ctx = canvasUI.getContext('2d');
Expand Down
17 changes: 17 additions & 0 deletions public/javascripts/SVValidate/css/svv.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
html,
body {
overflow: hidden;
}

html {
padding-top: 0px;
}

body {
padding-top: 30px;
}

#svv-application-holder {
background: rgba(255,255,255,1);
margin: 0 auto;
Expand Down Expand Up @@ -82,3 +95,7 @@ input[type="radio"] {
cursor: default;
opacity: 0.5;
}

.test-server-banner {
display: none;
}
9 changes: 5 additions & 4 deletions public/javascripts/SVValidate/src/Main.js
Original file line number Diff line number Diff line change
Expand Up @@ -247,10 +247,11 @@ function Main (param) {

// Use CSS zoom to scale the UI for users with high resolution screens.
// Has only been tested on Chrome and Safari. Firefox doesn't support CSS zoom.
if (!isMobile() && bowser.safari) {
svv.cssZoom = util.scaleUI();
window.addEventListener('resize', (e) => { svv.cssZoom = util.scaleUI(); });
}
// 9/8/2024 Update: This has been disabled in favor of an iframe-based scaling approach.
jsomeara marked this conversation as resolved.
Show resolved Hide resolved
// if (!isMobile() && bowser.safari) {
// svv.cssZoom = util.scaleUI();
// window.addEventListener('resize', (e) => { svv.cssZoom = util.scaleUI(); });
// }
}

// Gets all the text on the validation page for the correct language.
Expand Down
101 changes: 101 additions & 0 deletions public/javascripts/common/wrapperFrame.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
const iframe = document.getElementById('wrapper-frame');

// Get the element for the dev enviroment warning.
const testUserBanner = document.querySelector('.test-server-banner');

// Define a function 'iframeURLChange' that takes a callback to execute when the iframe's URL changes.
function iframeURLChange(callback) {

// Define a function 'unloadHandler' that will be called when the iframe's content is unloaded.
var unloadHandler = function () {

// Hide the iframe by setting its display to "none".
iframe.style.display = "none";

// Call the callback with the new iframe URL after a brief delay (0ms, to ensure execution timing).
setTimeout(() => {
callback(iframe.contentWindow.location.href);
}, 0);
};

// Define a function 'attachUnload' to add or replace the 'unload' event listener on the iframe's content window.
function attachUnload() {
// First, remove any existing 'unload' event listener to prevent duplication.
iframe.contentWindow.removeEventListener("unload", unloadHandler);

// Add the 'unload' event listener to the iframe's content window to call 'unloadHandler' when the content unloads.
iframe.contentWindow.addEventListener("unload", unloadHandler);
}

// Attach the unload event when the iframe finishes loading.
iframe.addEventListener("load", attachUnload);

// Immediately call 'attachUnload' in case the content has already loaded.
attachUnload();
}

// Define a function 'scaleIframeContent' to adjust the iframe content's scale to fit within the window.
function scaleIframeContent() {
// Set the device pixel ratio to 1 to avoid pixelation issues with Google Street View.
iframe.contentWindow.devicePixelRatio = 1;

// Access the document inside the iframe.
const iframeDocument = iframe.contentDocument || iframe.contentWindow.document;

// Find the element inside the iframe with the class 'tool-ui', which is the content to be scaled.
const contentElement = iframeDocument.querySelector('.tool-ui');

// Check if the content element exists before proceeding.
if (contentElement) {

// Get the width and height of the window (excluding 70px for fixed elements like navbar or the dev env warning).
const iframeWidth = window.innerWidth;
const iframeHeight = window.innerHeight - 70 - (testUserBanner ? testUserBanner.clientHeight : 0);

// Get the width and height of the content inside the iframe.
const contentWidth = contentElement.clientWidth;
const contentHeight = contentElement.clientHeight + 60; // Add 100px for extra padding.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, if I remove the +100 then part of the UI is cut off on the bottom. Not a big deal because we can play around with the values to get what we want, but ideally the math here would work as we expect!
Screenshot from 2024-09-30 14-34-43

I assume that part of this has to do with how we are adding padding-top to the body element? But not totally sure. Would be nice to have the padding-top make some sense, as I'm not really sure how it works or how it is supposed to right now!

I think that in an ideal world, we would be able to specify what percentage of the area below the navbar is to be used as white space both above and below the UI... It's not really that important to be able to do this, but whenever I'm fully invested in a section of the code like this, I like to try to refactor a bit to make things make more sense for the future. So if it doesn't feel like a huge lift, any movement in that direction would be great to see!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, I made those changes now!


// Calculate the scale factor based on the smallest ratio between available window size and content size.
const scale = Math.min(iframeWidth / contentWidth, iframeHeight / contentHeight);

// Apply the scaling transformation to the iframe based on the calculated scale factor.
iframe.style.transform = `scale(${scale})`;

// Adjust the iframe's width and height to scale the content correctly.
iframe.style.width = `${(1 / scale) * 100}vw`;
iframe.style.height = `calc(${(1 / scale) * 100}vh - ${(1 / scale) * 70}px)`;
}
}

// Attach the 'scaleIframeContent' function to the 'load' event on the iframe.
iframe.addEventListener('load', scaleIframeContent);

// Attach the 'scaleIframeContent' function to the 'resize' event on the window to resize the iframe when the window size changes.
window.addEventListener('resize', scaleIframeContent);

// Set the source of the iframe to the current window's URL.
iframe.src = window.location.href;

// Add an event listener for the iframe's 'load' event, to detect when the iframe's content finishes loading.
const firstLoadEventListener = iframe.addEventListener("load", function() {

// Call the 'iframeURLChange' function to handle URL changes, and update the window location with the new URL.
iframeURLChange(function (newURL) {
window.location.href = newURL;
});

// Remove this event listener after the first load event to prevent redundant calls.
iframe.removeEventListener("load", firstLoadEventListener);
});

setInterval(() => {
// This line is needed to lock the scroll to the top in rare cases where it can get messed up.
window.scrollTo(0, 0);

// Passthrough the 'svl' variable from the iframe's window object into the main window object.
window.svl = iframe.contentWindow.svl;

// Passthrough the 'InitialMissionInstruction' variable from the iframe's window object into the main window object.
window.InitialMissionInstruction = iframe.contentWindow.InitialMissionInstruction;
}, 100);
15 changes: 15 additions & 0 deletions public/stylesheets/wrapperFrame.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
html, body {
overflow: hidden;
min-height: 100vh;
}

#wrapper-frame {
width: 100vw;
height: calc(100vh - 70px);
border: none;
transform-origin: 0 0;
}

#mini-footer-audit {
jsomeara marked this conversation as resolved.
Show resolved Hide resolved
display: none;
}