implement session/preload on client

This commit is contained in:
Richard Harris
2019-02-02 16:13:07 -05:00
parent 91ea0335ae
commit c964500118

View File

@@ -3,6 +3,7 @@ import { stores } from '@sapper/internal';
import Root from '__ROOT__'; import Root from '__ROOT__';
import { preload as root_preload } from '__ROOT_PRELOAD__'; import { preload as root_preload } from '__ROOT_PRELOAD__';
import ErrorComponent from '__ERROR__'; import ErrorComponent from '__ERROR__';
import { writable } from 'svelte/store.mjs';
import { import {
Target, Target,
ScrollPosition, ScrollPosition,
@@ -18,6 +19,8 @@ import goto from './goto';
// injected at build time // injected at build time
declare const __IGNORE__, __COMPONENTS__, __PAGES__, __SAPPER__; declare const __IGNORE__, __COMPONENTS__, __PAGES__, __SAPPER__;
export const initial_data = typeof __SAPPER__ !== 'undefined' && __SAPPER__;
const ignore = __IGNORE__; const ignore = __IGNORE__;
export const components: ComponentLoader[] = __COMPONENTS__; export const components: ComponentLoader[] = __COMPONENTS__;
export const routes: Route[] = __PAGES__; export const routes: Route[] = __PAGES__;
@@ -28,6 +31,26 @@ let current_token: {};
let root_preloaded: Promise<any>; let root_preloaded: Promise<any>;
let current_branch = []; let current_branch = [];
const session = writable(initial_data && initial_data.session);
let $session;
let session_dirty: boolean;
session.subscribe(async value => {
$session = value;
if (!ready) return;
session_dirty = true;
const target = select_target(new URL(location.href));
const token = current_token = {};
const { redirect, props, branch } = await hydrate_target(target);
if (token !== current_token) return; // a secondary navigation happened while we were loading
await render(redirect, branch, props, target.page);
});
export let prefetching: { export let prefetching: {
href: string; href: string;
promise: Promise<{ redirect?: Redirect, data?: any }>; promise: Promise<{ redirect?: Redirect, data?: any }>;
@@ -56,8 +79,6 @@ export function set_cid(n) {
cid = n; cid = n;
} }
export const initial_data = typeof __SAPPER__ !== 'undefined' && __SAPPER__;
const _history = typeof history !== 'undefined' ? history : { const _history = typeof history !== 'undefined' ? history : {
pushState: (state: any, title: string, href: string) => {}, pushState: (state: any, title: string, href: string) => {},
replaceState: (state: any, title: string, href: string) => {}, replaceState: (state: any, title: string, href: string) => {},
@@ -137,13 +158,32 @@ export async function navigate(target: Target, id: number, noscroll?: boolean, h
const { redirect, props, branch } = await loaded; const { redirect, props, branch } = await loaded;
if (token !== current_token) return; // a secondary navigation happened while we were loading if (token !== current_token) return; // a secondary navigation happened while we were loading
if (redirect) return goto(redirect.location, { replaceState: true }); await render(redirect, branch, props, target.page);
await render(branch, props, target.page, scroll_history[id], noscroll, hash);
if (document.activeElement) document.activeElement.blur(); if (document.activeElement) document.activeElement.blur();
if (!noscroll) {
let scroll = scroll_history[id];
if (hash) {
// scroll is an element id (from a hash), we need to compute y.
const deep_linked = document.querySelector(hash);
if (deep_linked) {
scroll = {
x: 0,
y: deep_linked.getBoundingClientRect().top
};
}
}
scroll_history[cid] = scroll;
if (scroll) scrollTo(scroll.x, scroll.y);
}
} }
async function render(branch: any[], props: any, page: Page, scroll: ScrollPosition, noscroll: boolean, hash: string) { async function render(redirect: Redirect, branch: any[], props: any, page: Page) {
if (redirect) return goto(redirect.location, { replaceState: true });
stores.page.set(page); stores.page.set(page);
stores.preloading.set(false); stores.preloading.set(false);
@@ -165,31 +205,15 @@ async function render(branch: any[], props: any, page: Page, scroll: ScrollPosit
props: { props: {
Root, Root,
props, props,
session: __SAPPER__.session session
}, },
hydrate: true hydrate: true
}); });
} }
if (!noscroll) {
if (hash) {
// scroll is an element id (from a hash), we need to compute y.
const deep_linked = document.querySelector(hash);
if (deep_linked) {
scroll = {
x: 0,
y: deep_linked.getBoundingClientRect().top
};
}
}
scroll_history[cid] = scroll;
if (scroll) scrollTo(scroll.x, scroll.y);
}
current_branch = branch; current_branch = branch;
ready = true; ready = true;
session_dirty = false;
} }
export async function hydrate_target(target: Target): Promise<{ export async function hydrate_target(target: Target): Promise<{
@@ -221,7 +245,7 @@ export async function hydrate_target(target: Target): Promise<{
path: page.path, path: page.path,
query: page.query, query: page.query,
params: {} params: {}
}); }, $session);
} }
let branch; let branch;
@@ -231,7 +255,7 @@ export async function hydrate_target(target: Target): Promise<{
if (!part) return null; if (!part) return null;
const segment = segments[i]; const segment = segments[i];
if (current_branch[i] && current_branch[i].segment === segment) return current_branch[i]; if (!session_dirty && current_branch[i] && current_branch[i].segment === segment) return current_branch[i];
const { default: Component, preload } = await load_component(components[part.i]); const { default: Component, preload } = await load_component(components[part.i]);
@@ -242,7 +266,7 @@ export async function hydrate_target(target: Target): Promise<{
path: page.path, path: page.path,
query: page.query, query: page.query,
params: part.params ? part.params(target.match) : {} params: part.params ? part.params(target.match) : {}
}) }, $session)
: {}; : {};
} else { } else {
preloaded = initial_data.preloaded[i + 1]; preloaded = initial_data.preloaded[i + 1];