Merge pull request #279 from Johennes/feature/safari-viewport
Manually adapt UI when keyboard shows or hides on mobile Safari
This commit is contained in:
commit
25e0211ca1
5 changed files with 69 additions and 7 deletions
|
@ -351,7 +351,11 @@ async function buildCssLegacy(entryPath, urlMapper = null) {
|
|||
const preCss = await fs.readFile(entryPath, "utf8");
|
||||
const options = [
|
||||
postcssImport,
|
||||
cssvariables(),
|
||||
cssvariables({
|
||||
preserve: (declaration) => {
|
||||
return declaration.value.indexOf("var(--ios-") == 0;
|
||||
}
|
||||
}),
|
||||
autoprefixer({overrideBrowserslist: ["IE 11"], grid: "no-autoplace"}),
|
||||
flexbugsFixes()
|
||||
];
|
||||
|
|
|
@ -35,6 +35,7 @@ import {WorkerPool} from "./dom/WorkerPool.js";
|
|||
import {BlobHandle} from "./dom/BlobHandle.js";
|
||||
import {hasReadPixelPermission, ImageHandle, VideoHandle} from "./dom/ImageHandle.js";
|
||||
import {downloadInIframe} from "./dom/download.js";
|
||||
import {Disposables} from "../../utils/Disposables.js";
|
||||
|
||||
function addScript(src) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
|
@ -83,6 +84,44 @@ async function loadOlmWorker(config) {
|
|||
return olmWorker;
|
||||
}
|
||||
|
||||
// needed for mobile Safari which shifts the layout viewport up without resizing it
|
||||
// when the keyboard shows (see https://bugs.webkit.org/show_bug.cgi?id=141832)
|
||||
function adaptUIOnVisualViewportResize(container) {
|
||||
if (!window.visualViewport) {
|
||||
return;
|
||||
}
|
||||
const handler = () => {
|
||||
const sessionView = container.querySelector('.SessionView');
|
||||
if (!sessionView) {
|
||||
return;
|
||||
}
|
||||
|
||||
const scrollable = container.querySelector('.bottom-aligned-scroll');
|
||||
let scrollTopBefore, heightBefore, heightAfter;
|
||||
|
||||
if (scrollable) {
|
||||
scrollTopBefore = scrollable.scrollTop;
|
||||
heightBefore = scrollable.offsetHeight;
|
||||
}
|
||||
|
||||
// Ideally we'd use window.visualViewport.offsetTop but that seems to occasionally lag
|
||||
// behind (last tested on iOS 14.4 simulator) so we have to compute the offset manually
|
||||
const offsetTop = sessionView.offsetTop + sessionView.offsetHeight - window.visualViewport.height;
|
||||
|
||||
container.style.setProperty('--ios-viewport-height', window.visualViewport.height.toString() + 'px');
|
||||
container.style.setProperty('--ios-viewport-top', offsetTop.toString() + 'px');
|
||||
|
||||
if (scrollable) {
|
||||
heightAfter = scrollable.offsetHeight;
|
||||
scrollable.scrollTop = scrollTopBefore + heightBefore - heightAfter;
|
||||
}
|
||||
};
|
||||
window.visualViewport.addEventListener('resize', handler);
|
||||
return () => {
|
||||
window.visualViewport.removeEventListener('resize', handler);
|
||||
};
|
||||
}
|
||||
|
||||
export class Platform {
|
||||
constructor(container, config, cryptoExtras = null, options = null) {
|
||||
this._config = config;
|
||||
|
@ -115,6 +154,10 @@ export class Platform {
|
|||
}
|
||||
const isIE11 = !!window.MSInputMethodContext && !!document.documentMode;
|
||||
this.isIE11 = isIE11;
|
||||
// From https://stackoverflow.com/questions/9038625/detect-if-device-is-ios/9039885
|
||||
const isIOS = /iPad|iPhone|iPod/.test(navigator.platform) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1) && !window.MSStream;
|
||||
this.isIOS = isIOS;
|
||||
this._disposables = new Disposables();
|
||||
}
|
||||
|
||||
get updateService() {
|
||||
|
@ -139,6 +182,13 @@ export class Platform {
|
|||
if (this.isIE11) {
|
||||
this._container.className += " legacy";
|
||||
}
|
||||
if (this.isIOS) {
|
||||
this._container.className += " ios";
|
||||
const disposable = adaptUIOnVisualViewportResize(this._container);
|
||||
if (disposable) {
|
||||
this._disposables.track(disposable);
|
||||
}
|
||||
}
|
||||
window.__hydrogenViewModel = vm;
|
||||
const view = new RootView(vm);
|
||||
this._container.appendChild(view.mount());
|
||||
|
@ -156,7 +206,7 @@ export class Platform {
|
|||
if (navigator.msSaveBlob) {
|
||||
navigator.msSaveBlob(blobHandle.nativeBlob, filename);
|
||||
} else {
|
||||
downloadInIframe(this._container, this._config.downloadSandbox, blobHandle, filename);
|
||||
downloadInIframe(this._container, this._config.downloadSandbox, blobHandle, filename, this.isIOS);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -205,4 +255,8 @@ export class Platform {
|
|||
get version() {
|
||||
return window.HYDROGEN_VERSION;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this._disposables.dispose();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,10 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
// From https://stackoverflow.com/questions/9038625/detect-if-device-is-ios/9039885
|
||||
const isIOS = /iPad|iPhone|iPod/.test(navigator.platform) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1) && !window.MSStream;
|
||||
|
||||
export async function downloadInIframe(container, iframeSrc, blobHandle, filename) {
|
||||
export async function downloadInIframe(container, iframeSrc, blobHandle, filename, isIOS) {
|
||||
let iframe = container.querySelector("iframe.downloadSandbox");
|
||||
if (!iframe) {
|
||||
iframe = document.createElement("iframe");
|
||||
|
|
|
@ -54,6 +54,13 @@ main {
|
|||
min-width: 0;
|
||||
}
|
||||
|
||||
/* resize and reposition session view to account for mobile Safari which shifts
|
||||
the layout viewport up without resizing it when the keyboard shows */
|
||||
.hydrogen.ios .SessionView {
|
||||
height: var(--ios-viewport-height, 100%);
|
||||
top: var(--ios-viewport-top, 0);
|
||||
}
|
||||
|
||||
/* hide back button in middle section by default */
|
||||
.middle .close-middle { display: none; }
|
||||
/* mobile layout */
|
||||
|
|
|
@ -40,7 +40,7 @@ function viewClassForEntry(entry) {
|
|||
export class TimelineList extends ListView {
|
||||
constructor(viewModel) {
|
||||
const options = {
|
||||
className: "Timeline",
|
||||
className: "Timeline bottom-aligned-scroll",
|
||||
list: viewModel.tiles,
|
||||
}
|
||||
super(options, entry => {
|
||||
|
|
Reference in a new issue