UX: Refactor iOS composer layout

This addresses the following issues:
- on iPad, with keyboard attached, the composer is no longer forced to full screen
- on iPad, with keyboard attached, the topic no longer scrolls when starting a
  reply and then cancelling it
- switching between inputs and buttons (formatting, emojis, categories/tags, etc.) no longer
  causes layout to bounce around
This commit is contained in:
Penar Musaraj 2019-10-08 11:16:41 -04:00
parent d527f3a723
commit c3a5a8e095
5 changed files with 90 additions and 98 deletions

View File

@ -142,11 +142,6 @@ export default Ember.Component.extend(KeyEnterEscape, {
viewportResize() {
const composerVH = window.visualViewport.height * 0.01;
if (window.visualViewport.height !== window.innerHeight) {
document.documentElement.classList.add("keyboard-visible");
} else {
document.documentElement.classList.remove("keyboard-visible");
}
document.documentElement.style.setProperty(
"--composer-vh",
`${composerVH}px`

View File

@ -85,18 +85,12 @@ function positioningWorkaround($fixedElement) {
const fixedElement = $fixedElement[0];
const oldHeight = fixedElement.style.height;
var done = false;
var originalScrollTop = 0;
let lastTouchedElement = null;
positioningWorkaround.blur = function(evt) {
if (workaroundActive) {
done = true;
$("#main-outlet").show();
$("header").show();
fixedElement.style.position = "";
fixedElement.style.top = "";
$("body").removeClass("ios-safari-composer-hacks");
if (!iOSWithVisualViewport()) {
fixedElement.style.height = oldHeight;
@ -116,15 +110,17 @@ function positioningWorkaround($fixedElement) {
};
var blurredNow = function(evt) {
// we cannot use evt.relatedTarget to get the last focused element in safari iOS
// document.activeElement is also unreliable (iOS does not mark buttons as focused)
// so instead, we store the last touched element and check against it
if (
!done &&
evt.srcElement !== document.activeElement &&
$(document.activeElement)
.parents()
.toArray()
.indexOf(fixedElement) > -1
lastTouchedElement &&
($(lastTouchedElement).hasClass("select-kit-header") ||
["span", "svg", "button"].includes(
lastTouchedElement.nodeName.toLowerCase()
))
) {
// something in focus so skip
return;
}
@ -134,60 +130,77 @@ function positioningWorkaround($fixedElement) {
var blurred = debounce(blurredNow, 250);
var positioningHack = function(evt) {
done = false;
// we need this, otherwise changing focus means we never clear
this.addEventListener("blur", blurred);
if (fixedElement.style.top === "0px") {
if (this !== document.activeElement) {
evt.preventDefault();
// this tricks safari into assuming current input is at top of the viewport
// via https://stackoverflow.com/questions/38017771/mobile-safari-prevent-scroll-page-when-focus-on-input
this.style.transform = "translateY(-200px)";
this.focus();
let _this = this;
setTimeout(function() {
_this.style.transform = "none";
}, 50);
}
return;
}
// don't trigger keyboard on disabled element (happens when a category is required)
if (this.disabled) {
return;
}
// resets focus out of select-kit elements
// might become redundant after select-kit refactoring
$fixedElement.find(".select-kit.is-expanded > button").trigger("click");
$fixedElement
.find(".select-kit > button.is-focused")
.removeClass("is-focused");
originalScrollTop = $(window).scrollTop();
// take care of body
$("#main-outlet").hide();
$("header").hide();
$(window).scrollTop(0);
let i = 20;
let interval = setInterval(() => {
$(window).scrollTop(0);
if (i-- === 0) {
clearInterval(interval);
setTimeout(function() {
if (iOSWithVisualViewport()) {
// disable hacks when using a hardware keyboard
// by default, a hardware keyboard will show the keyboard accessory bar
// whose height is currently 55px (using 75 for a bit of a buffer)
let heightDiff = window.innerHeight - window.visualViewport.height;
if (heightDiff < 75) {
return;
}
}
}, 10);
fixedElement.style.top = "0px";
if (fixedElement.style.top === "0px") {
if (this !== document.activeElement) {
evt.preventDefault();
if (!iOSWithVisualViewport()) {
const height = calcHeight();
fixedElement.style.height = height + "px";
$(fixedElement).addClass("no-transition");
// this tricks safari into assuming current input is at top of the viewport
// via https://stackoverflow.com/questions/38017771/mobile-safari-prevent-scroll-page-when-focus-on-input
this.style.transform = "translateY(-200px)";
this.focus();
let _this = this;
setTimeout(function() {
_this.style.transform = "none";
}, 30);
}
return;
}
// don't trigger keyboard on disabled element (happens when a category is required)
if (this.disabled) {
return;
}
$("body").addClass("ios-safari-composer-hacks");
$(window).scrollTop(0);
let i = 20;
let interval = setInterval(() => {
$(window).scrollTop(0);
if (i-- === 0) {
clearInterval(interval);
}
}, 10);
if (!iOSWithVisualViewport()) {
const height = calcHeight();
fixedElement.style.height = height + "px";
$(fixedElement).addClass("no-transition");
}
evt.preventDefault();
this.focus();
workaroundActive = true;
}, 350);
};
var lastTouched = function(evt) {
if (evt && evt.target) {
lastTouchedElement = evt.target;
}
evt.preventDefault();
this.focus();
workaroundActive = true;
};
function attachTouchStart(elem, fn) {
@ -198,30 +211,8 @@ function positioningWorkaround($fixedElement) {
}
const checkForInputs = debounce(function() {
$fixedElement
.find(
"button:not(.hide-preview),a:not(.mobile-file-upload):not(.toggle-toolbar)"
)
.each(function(idx, elem) {
if ($(elem).parents(".emoji-picker").length > 0) {
return;
}
attachTouchStart(fixedElement, lastTouched);
if ($(elem).parents(".autocomplete").length > 0) {
return;
}
if ($(elem).parents(".d-editor-button-bar").length > 0) {
return;
}
attachTouchStart(this, function(evt) {
done = true;
$(document.activeElement).blur();
evt.preventDefault();
$(this).click();
});
});
$fixedElement.find("input[type=text],textarea").each(function() {
attachTouchStart(this, positioningHack);
});

View File

@ -483,3 +483,19 @@ div.ac-wrap {
opacity: 0;
}
}
body.ios-safari-composer-hacks {
#main-outlet,
header,
.grippie,
html:not(.fullscreen-composer) & .toggle-fullscreen {
display: none;
}
#reply-control {
top: 0px;
&.open {
height: calc(var(--composer-vh, 1vh) * 100);
}
}
}

View File

@ -230,16 +230,6 @@ a.toggle-preview {
text-align: right;
}
html.keyboard-visible {
.grippie,
&:not(.fullscreen-composer) .toggle-fullscreen {
display: none;
}
#reply-control.open {
height: calc(var(--composer-vh, 1vh) * 100);
}
}
// fullscreen composer styles
.fullscreen-composer {
overflow: hidden;

View File

@ -24,7 +24,7 @@
}
}
html.keyboard-visible &.open {
body.ios-safari-composer-hacks &.open {
height: calc(var(--composer-vh, 1vh) * 100);
.reply-area {
padding-bottom: 0px;