bookish-press 0.4.22 → 0.4.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/Auth.svelte +9 -11
- package/components/BookPreview.svelte +12 -14
- package/components/app/Feedback.svelte +12 -10
- package/components/app/Head.svelte +4 -1
- package/components/app/Head.svelte.d.ts +6 -15
- package/components/app/Paragraph.svelte +1 -0
- package/components/app/{Writing.svelte → Section.svelte} +0 -1
- package/components/app/Section.svelte.d.ts +27 -0
- package/components/app/TextInput.svelte +1 -0
- package/components/chapter/Block.svelte +3 -1
- package/components/chapter/CaptionedCode.svelte +2 -2
- package/components/chapter/ChapterBody.svelte +2 -1
- package/components/chapter/ChapterBody.svelte.d.ts +1 -0
- package/components/chapter/Comment.svelte +2 -2
- package/components/chapter/Embed.svelte +3 -3
- package/components/chapter/Embed.svelte.d.ts +1 -0
- package/components/chapter/Figure.svelte +2 -2
- package/components/chapter/Format.svelte +5 -3
- package/components/chapter/Format.svelte.d.ts +1 -0
- package/components/chapter/InlineCode.svelte +2 -2
- package/components/chapter/Label.svelte +2 -2
- package/components/chapter/Link.svelte +2 -2
- package/components/chapter/Marginal.svelte +2 -2
- package/components/chapter/Paragraph.svelte +5 -0
- package/components/chapter/Table.svelte +2 -2
- package/components/chapter/Text.svelte +2 -2
- package/components/editor/BookishEditor.svelte +51 -28
- package/components/editor/BookishEditor.svelte.d.ts +4 -1
- package/components/editor/CaretView.svelte +3 -1
- package/components/editor/CaretView.svelte.d.ts +1 -0
- package/components/editor/ConfirmButton.svelte +1 -1
- package/components/editor/Note.svelte +1 -0
- package/components/editor/Permissions.svelte +95 -0
- package/components/editor/Permissions.svelte.d.ts +20 -0
- package/components/editor/Toolbar.svelte +0 -1
- package/components/editor/ToolbarGroup.svelte +0 -1
- package/components/page/Acknowledgements.svelte +13 -11
- package/components/page/Authors.svelte +5 -8
- package/components/page/Authors.svelte.d.ts +1 -0
- package/components/page/BookLoader.svelte +59 -0
- package/components/page/BookLoader.svelte.d.ts +16 -0
- package/components/page/BulkReferenceEditor.svelte +8 -8
- package/components/page/Chapter.svelte +43 -10
- package/components/page/Contexts.d.ts +11 -2
- package/components/page/Contexts.js +85 -3
- package/components/page/DefinitionView.svelte +18 -8
- package/components/page/Description.svelte +12 -9
- package/components/page/Edition.svelte +15 -13
- package/components/page/Edition.svelte.d.ts +0 -1
- package/components/page/EditionLoader.svelte +99 -0
- package/components/page/EditionLoader.svelte.d.ts +18 -0
- package/components/page/Editions.svelte +92 -143
- package/components/page/Glossary.svelte +13 -6
- package/components/page/Header.svelte +22 -20
- package/components/page/Header.svelte.d.ts +2 -0
- package/components/page/Index.svelte +6 -4
- package/components/page/Instructions.svelte +1 -2
- package/components/page/Instructions.svelte.d.ts +3 -1
- package/components/page/License.svelte +20 -12
- package/components/page/Media.svelte +14 -12
- package/components/page/Publish.svelte +120 -0
- package/components/page/Publish.svelte.d.ts +14 -0
- package/components/page/Reference.svelte +16 -17
- package/components/page/References.svelte +8 -3
- package/components/page/Search.svelte +3 -1
- package/components/page/Status.svelte +18 -19
- package/components/page/SubdomainEditor.svelte +3 -2
- package/components/page/TableOfContents.svelte +70 -30
- package/components/page/TableOfContentsRow.svelte +4 -4
- package/components/page/ThemeEditor.svelte +8 -5
- package/components/page/Unknown.svelte +5 -2
- package/models/CRUD.d.ts +19 -11
- package/models/CRUD.js +177 -122
- package/models/Firebase.js +12 -8
- package/models/book/Book.d.ts +19 -14
- package/models/book/Book.js +47 -54
- package/models/book/Chapter.d.ts +5 -1
- package/models/book/Chapter.js +24 -15
- package/models/book/Edition.d.ts +31 -7
- package/models/book/Edition.js +101 -32
- package/models/chapter/Parser.d.ts +2 -2
- package/models/chapter/Parser.js +26 -24
- package/package.json +18 -15
- package/components/app/Writing.svelte.d.ts +0 -27
- package/models/loadEdition.d.ts +0 -5
- package/models/loadEdition.js +0 -20
|
@@ -11,7 +11,7 @@ import ChapterBody from '../chapter/ChapterBody.svelte';
|
|
|
11
11
|
import PossibleReference from './PossibleReference.svelte';
|
|
12
12
|
import ChapterNumber from './ChapterNumber.svelte';
|
|
13
13
|
import Problem from '../chapter/Problem.svelte';
|
|
14
|
-
import { CHAPTER, getBook, getEdition,
|
|
14
|
+
import { CHAPTER, getAuth, getBook, getEdition, isBookEditable, isChapterEditable, isEditionPartiallyEditable, getLeasee, lease, setChapter, isEditionEditable, } from './Contexts';
|
|
15
15
|
import { writable } from 'svelte/store';
|
|
16
16
|
import { onMount, setContext } from 'svelte';
|
|
17
17
|
import { hideOutlineIfObscured, layoutMarginals } from './margins';
|
|
@@ -20,11 +20,13 @@ import { goto } from '$app/navigation';
|
|
|
20
20
|
import ChapterNode from '../../models/chapter/ChapterNode';
|
|
21
21
|
import Muted from './Muted.svelte';
|
|
22
22
|
import Title from './Title.svelte';
|
|
23
|
+
import PageHeader from './PageHeader.svelte';
|
|
24
|
+
import Permissions from '../editor/Permissions.svelte';
|
|
23
25
|
export let chapter;
|
|
24
26
|
export let print = false;
|
|
25
27
|
let book = getBook();
|
|
26
28
|
let edition = getEdition();
|
|
27
|
-
let
|
|
29
|
+
let auth = getAuth();
|
|
28
30
|
// Keep track of the scroll position to facilitate reading during reloads.
|
|
29
31
|
function rememberPosition() {
|
|
30
32
|
localStorage.setItem('scrollposition', '' + window.scrollY);
|
|
@@ -170,7 +172,7 @@ $: chapterSection = $edition?.getChapterSection(chapterID);
|
|
|
170
172
|
$: chapterAST = $edition ? chapter.getAST($edition) : undefined;
|
|
171
173
|
$: citations = chapterAST ? chapterAST.getCitations() : undefined;
|
|
172
174
|
$: editionNumber =
|
|
173
|
-
$book && $edition ? $edition.getEditionNumber(
|
|
175
|
+
$book && $edition ? $edition.getEditionNumber() : undefined;
|
|
174
176
|
let chapterStore = writable();
|
|
175
177
|
setContext(CHAPTER, chapterStore);
|
|
176
178
|
$: chapterStore.set({
|
|
@@ -180,6 +182,12 @@ $: chapterStore.set({
|
|
|
180
182
|
marginal: marginal,
|
|
181
183
|
layoutMarginals: layoutMarginals,
|
|
182
184
|
});
|
|
185
|
+
let editable = isBookEditable() ||
|
|
186
|
+
isEditionEditable() ||
|
|
187
|
+
isChapterEditable() ||
|
|
188
|
+
($auth !== undefined &&
|
|
189
|
+
$auth.user !== null &&
|
|
190
|
+
chapter.isEditor($auth.user.uid));
|
|
183
191
|
</script>
|
|
184
192
|
|
|
185
193
|
{#if $edition}
|
|
@@ -188,6 +196,8 @@ $: chapterStore.set({
|
|
|
188
196
|
title={`${$edition.getTitle()} - ${chapter.getTitle()}`}
|
|
189
197
|
>
|
|
190
198
|
<Header
|
|
199
|
+
editable={isChapterEditable()}
|
|
200
|
+
id={chapter.id}
|
|
191
201
|
header={chapter.getTitle()}
|
|
192
202
|
label="Chapter title"
|
|
193
203
|
tags={$edition.getTags()}
|
|
@@ -266,6 +276,7 @@ $: chapterStore.set({
|
|
|
266
276
|
</svelte:fragment>
|
|
267
277
|
<!-- If there are chapter authors, render those, otherwise use the book authors -->
|
|
268
278
|
<Authors
|
|
279
|
+
editable={isChapterEditable()}
|
|
269
280
|
slot="after"
|
|
270
281
|
authors={chapter.getAuthors()}
|
|
271
282
|
inheritedAuthors={$edition.getAuthors()}
|
|
@@ -281,13 +292,13 @@ $: chapterStore.set({
|
|
|
281
292
|
/>
|
|
282
293
|
</Header>
|
|
283
294
|
|
|
284
|
-
<Instructions>
|
|
295
|
+
<Instructions {editable}>
|
|
285
296
|
Edit your chapter's title, authors, and cover image above. You can
|
|
286
|
-
also change the ID of the chapter, which
|
|
287
|
-
your chapter text below,
|
|
288
|
-
toolbar above to
|
|
297
|
+
also change the ID of the chapter, which is the text that appears in
|
|
298
|
+
the chapter URL. Write your chapter text below, formatting options
|
|
299
|
+
in the toolbar above to add headers, lists, tables, comments,
|
|
289
300
|
citations, footnotes, and more. It's okay to try things, you can
|
|
290
|
-
always undo
|
|
301
|
+
always undo. Saves are automatic.
|
|
291
302
|
</Instructions>
|
|
292
303
|
|
|
293
304
|
<!-- Render the chapter body, passing some context -->
|
|
@@ -295,7 +306,8 @@ $: chapterStore.set({
|
|
|
295
306
|
{#if chapterAST}
|
|
296
307
|
{#if editable}
|
|
297
308
|
<BookishEditor
|
|
298
|
-
|
|
309
|
+
text={chapterAST.toBookdown()}
|
|
310
|
+
parser={(text) => Parser.parseChapter($edition, text)}
|
|
299
311
|
save={(ast) =>
|
|
300
312
|
ast instanceof ChapterNode
|
|
301
313
|
? setChapter(
|
|
@@ -307,7 +319,9 @@ $: chapterStore.set({
|
|
|
307
319
|
chapter={true}
|
|
308
320
|
autofocus
|
|
309
321
|
component={ChapterBody}
|
|
310
|
-
placeholder="
|
|
322
|
+
placeholder="write here"
|
|
323
|
+
leasee={getLeasee(auth, edition, chapter.id)}
|
|
324
|
+
lease={(lock) => lease(auth, edition, chapter.id, lock)}
|
|
311
325
|
/>
|
|
312
326
|
{:else}
|
|
313
327
|
<ChapterBody node={chapterAST} />
|
|
@@ -340,6 +354,25 @@ $: chapterStore.set({
|
|
|
340
354
|
{/each}
|
|
341
355
|
</ol>
|
|
342
356
|
{/if}
|
|
357
|
+
|
|
358
|
+
{#if editable || isEditionPartiallyEditable()}
|
|
359
|
+
<PageHeader>Editors</PageHeader>
|
|
360
|
+
<Instructions {editable}
|
|
361
|
+
>These authors can edit this chapter. Add an email here to
|
|
362
|
+
provide chapter-level edit permissions.</Instructions
|
|
363
|
+
>
|
|
364
|
+
|
|
365
|
+
<Permissions
|
|
366
|
+
uids={chapter.uids}
|
|
367
|
+
inheriteduids={Array.from(
|
|
368
|
+
new Set([...$edition.uids, ...($book ? $book.uids : [])])
|
|
369
|
+
)}
|
|
370
|
+
atleastone={false}
|
|
371
|
+
writable={isChapterEditable()}
|
|
372
|
+
change={(uids) =>
|
|
373
|
+
setChapter(edition, chapter, chapter.withEditors(uids))}
|
|
374
|
+
/>
|
|
375
|
+
{/if}
|
|
343
376
|
</Page>
|
|
344
377
|
{/if}
|
|
345
378
|
|
|
@@ -6,6 +6,7 @@ import type Edition from '../../models/book/Edition';
|
|
|
6
6
|
import type Authentication from '../Authentication';
|
|
7
7
|
import type CaretState from '../editor/CaretState';
|
|
8
8
|
import type ChapterContext from './ChapterContext';
|
|
9
|
+
import type { DocumentReference } from 'firebase/firestore';
|
|
9
10
|
export type DarkModeStore = Writable<boolean>;
|
|
10
11
|
export declare const DARK_MODE: unique symbol;
|
|
11
12
|
export declare function getDarkMode(): DarkModeStore;
|
|
@@ -18,6 +19,10 @@ export declare function getBook(): BookStore;
|
|
|
18
19
|
export type EditionStore = Writable<Edition | undefined>;
|
|
19
20
|
export declare const EDITION: unique symbol;
|
|
20
21
|
export declare function getEdition(): EditionStore;
|
|
22
|
+
export type ChapterText = [DocumentReference, string][];
|
|
23
|
+
export type ChapterTextStore = Writable<ChapterText | undefined>;
|
|
24
|
+
export declare const CHAPTERTEXT: unique symbol;
|
|
25
|
+
export declare function getChapterText(): ChapterTextStore;
|
|
21
26
|
export type ChapterStore = Writable<ChapterContext>;
|
|
22
27
|
export declare const CHAPTER: unique symbol;
|
|
23
28
|
export declare function getChapter(): ChapterStore;
|
|
@@ -26,8 +31,10 @@ export declare function getChapter(): ChapterStore;
|
|
|
26
31
|
* global edition store with the new edition, propogating the changes everywhere.
|
|
27
32
|
*/
|
|
28
33
|
export declare function setChapter(store: EditionStore, previous: Chapter, chapter: Chapter): void;
|
|
29
|
-
export declare
|
|
30
|
-
export declare function
|
|
34
|
+
export declare function isChapterEditable(): boolean;
|
|
35
|
+
export declare function isEditionEditable(): boolean;
|
|
36
|
+
export declare function isEditionPartiallyEditable(): boolean;
|
|
37
|
+
export declare function isBookEditable(): boolean;
|
|
31
38
|
export declare const BASE: unique symbol;
|
|
32
39
|
export type BaseStore = Writable<string>;
|
|
33
40
|
export declare function getBase(): BaseStore;
|
|
@@ -36,3 +43,5 @@ export declare function getCaret(): Writable<CaretState | undefined>;
|
|
|
36
43
|
export declare const STATUS: unique symbol;
|
|
37
44
|
export type StatusStore = Writable<BookSaveStatus>;
|
|
38
45
|
export declare function getStatus(): StatusStore;
|
|
46
|
+
export declare function lease(authStore: AuthStore, editionStore: EditionStore, contentID: string, lock: boolean): Promise<boolean>;
|
|
47
|
+
export declare function getLeasee(authStore: AuthStore, editionStore: EditionStore, contentID: string): string | boolean;
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
// as the resulting value is coerced to Type even if it's not that value.
|
|
5
5
|
import { getContext } from 'svelte';
|
|
6
6
|
import { get } from 'svelte/store';
|
|
7
|
+
import { page } from '$app/stores';
|
|
8
|
+
import { updateLock } from '../../models/CRUD';
|
|
7
9
|
export const DARK_MODE = Symbol('dark');
|
|
8
10
|
export function getDarkMode() {
|
|
9
11
|
return getContext(DARK_MODE);
|
|
@@ -20,6 +22,10 @@ export const EDITION = Symbol('edition');
|
|
|
20
22
|
export function getEdition() {
|
|
21
23
|
return getContext(EDITION);
|
|
22
24
|
}
|
|
25
|
+
export const CHAPTERTEXT = Symbol('chaptertext');
|
|
26
|
+
export function getChapterText() {
|
|
27
|
+
return getContext(CHAPTERTEXT);
|
|
28
|
+
}
|
|
23
29
|
export const CHAPTER = Symbol('chapter');
|
|
24
30
|
export function getChapter() {
|
|
25
31
|
return getContext(CHAPTER);
|
|
@@ -33,9 +39,53 @@ export function setChapter(store, previous, chapter) {
|
|
|
33
39
|
if (edition)
|
|
34
40
|
store.set(edition.withRevisedChapter(previous, chapter));
|
|
35
41
|
}
|
|
36
|
-
export
|
|
37
|
-
|
|
38
|
-
|
|
42
|
+
export function isChapterEditable() {
|
|
43
|
+
if (!get(page).route.id?.startsWith('/write'))
|
|
44
|
+
return false;
|
|
45
|
+
const auth = get(getAuth());
|
|
46
|
+
const chapter = get(getChapter());
|
|
47
|
+
const edition = get(getEdition());
|
|
48
|
+
const book = get(getBook());
|
|
49
|
+
return (chapter !== undefined &&
|
|
50
|
+
edition !== undefined &&
|
|
51
|
+
book !== undefined &&
|
|
52
|
+
auth !== undefined &&
|
|
53
|
+
auth.user !== null &&
|
|
54
|
+
(chapter.chapter.isEditor(auth.user.uid) ||
|
|
55
|
+
edition.isEditor(auth.user.uid) ||
|
|
56
|
+
book.isEditor(auth.user.uid)));
|
|
57
|
+
}
|
|
58
|
+
export function isEditionEditable() {
|
|
59
|
+
if (!get(page).route.id?.startsWith('/write'))
|
|
60
|
+
return false;
|
|
61
|
+
const auth = get(getAuth());
|
|
62
|
+
const edition = get(getEdition());
|
|
63
|
+
const book = get(getBook());
|
|
64
|
+
return (edition !== undefined &&
|
|
65
|
+
book !== undefined &&
|
|
66
|
+
auth !== undefined &&
|
|
67
|
+
auth.user !== null &&
|
|
68
|
+
(edition.isEditor(auth.user.uid) || book.isEditor(auth.user.uid)));
|
|
69
|
+
}
|
|
70
|
+
export function isEditionPartiallyEditable() {
|
|
71
|
+
if (!get(page).route.id?.startsWith('/write'))
|
|
72
|
+
return false;
|
|
73
|
+
const auth = get(getAuth());
|
|
74
|
+
const edition = get(getEdition());
|
|
75
|
+
return (edition !== undefined &&
|
|
76
|
+
auth !== undefined &&
|
|
77
|
+
auth.user !== null &&
|
|
78
|
+
edition.isChapterEditor(auth.user.uid));
|
|
79
|
+
}
|
|
80
|
+
export function isBookEditable() {
|
|
81
|
+
if (!get(page).route.id?.startsWith('/write'))
|
|
82
|
+
return false;
|
|
83
|
+
const auth = get(getAuth());
|
|
84
|
+
const book = get(getBook());
|
|
85
|
+
return (book !== undefined &&
|
|
86
|
+
auth !== undefined &&
|
|
87
|
+
auth.user !== null &&
|
|
88
|
+
book.isEditor(auth.user.uid));
|
|
39
89
|
}
|
|
40
90
|
export const BASE = Symbol('base');
|
|
41
91
|
export function getBase() {
|
|
@@ -49,3 +99,35 @@ export const STATUS = Symbol('status');
|
|
|
49
99
|
export function getStatus() {
|
|
50
100
|
return getContext(STATUS);
|
|
51
101
|
}
|
|
102
|
+
export async function lease(authStore, editionStore, contentID, lock) {
|
|
103
|
+
const edition = get(editionStore);
|
|
104
|
+
const auth = get(authStore);
|
|
105
|
+
if (edition && auth && auth.user) {
|
|
106
|
+
await updateLock(edition, auth.user.uid, contentID, lock);
|
|
107
|
+
const latestEdition = get(editionStore);
|
|
108
|
+
const latestAuth = get(authStore);
|
|
109
|
+
if (latestEdition && latestAuth && latestAuth.user) {
|
|
110
|
+
editionStore.set(latestEdition.withLock(latestAuth.user.uid, contentID, lock));
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
else
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
else
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
export function getLeasee(authStore, editionStore, contentID) {
|
|
120
|
+
const edition = get(editionStore);
|
|
121
|
+
const auth = get(authStore);
|
|
122
|
+
if (edition !== undefined && auth !== undefined && auth.user !== null) {
|
|
123
|
+
const leasee = edition.getLeasee(contentID);
|
|
124
|
+
if (leasee === undefined)
|
|
125
|
+
return false;
|
|
126
|
+
else if (leasee === auth.user.uid)
|
|
127
|
+
return true;
|
|
128
|
+
else
|
|
129
|
+
return leasee;
|
|
130
|
+
}
|
|
131
|
+
else
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
@@ -5,12 +5,13 @@ import BookishEditor from '../editor/BookishEditor.svelte';
|
|
|
5
5
|
import Format from '../chapter/Format.svelte';
|
|
6
6
|
import Note from '../editor/Note.svelte';
|
|
7
7
|
import { afterUpdate } from 'svelte';
|
|
8
|
-
import { getEdition,
|
|
8
|
+
import { getAuth, getEdition, getLeasee, isChapterEditable, lease, } from './Contexts';
|
|
9
9
|
import Button from '../app/Button.svelte';
|
|
10
10
|
export let id;
|
|
11
11
|
export let definition;
|
|
12
|
+
let auth = getAuth();
|
|
12
13
|
let edition = getEdition();
|
|
13
|
-
let editable =
|
|
14
|
+
let editable = isChapterEditable();
|
|
14
15
|
// Focus after adding a new synonym.
|
|
15
16
|
let newSynonym = false;
|
|
16
17
|
let synonymsEditor = null;
|
|
@@ -25,7 +26,6 @@ afterUpdate(() => {
|
|
|
25
26
|
newSynonym = false;
|
|
26
27
|
}
|
|
27
28
|
});
|
|
28
|
-
$: format = Parser.parseFormat(undefined, definition.definition).withTextIfEmpty();
|
|
29
29
|
function addSynonym() {
|
|
30
30
|
if ($edition) {
|
|
31
31
|
edition.set($edition.withEditedDefinition(id, {
|
|
@@ -46,7 +46,7 @@ $: syns = definition.synonyms || [];
|
|
|
46
46
|
<TextEditor
|
|
47
47
|
text={definition.phrase}
|
|
48
48
|
label={'Glossary phrase editor.'}
|
|
49
|
-
placeholder="
|
|
49
|
+
placeholder="phrase"
|
|
50
50
|
valid={(text) => {
|
|
51
51
|
if (text.length === 0) return "phrase can't be empty";
|
|
52
52
|
}}
|
|
@@ -80,7 +80,9 @@ $: syns = definition.synonyms || [];
|
|
|
80
80
|
<dd class="term">
|
|
81
81
|
{#if editable && edition}
|
|
82
82
|
<BookishEditor
|
|
83
|
-
|
|
83
|
+
text={definition.definition}
|
|
84
|
+
parser={(text) =>
|
|
85
|
+
Parser.parseFormat(undefined, text).withTextIfEmpty()}
|
|
84
86
|
save={(node) =>
|
|
85
87
|
$edition
|
|
86
88
|
? edition.set(
|
|
@@ -93,12 +95,20 @@ $: syns = definition.synonyms || [];
|
|
|
93
95
|
: undefined}
|
|
94
96
|
chapter={false}
|
|
95
97
|
component={Format}
|
|
96
|
-
placeholder="
|
|
98
|
+
placeholder="definition"
|
|
99
|
+
leasee={getLeasee(auth, edition, `definition-${id}`)}
|
|
100
|
+
lease={(lock) =>
|
|
101
|
+
lease(auth, edition, `definition-${id}`, lock)}
|
|
97
102
|
/>
|
|
98
103
|
{:else if definition.definition === ''}
|
|
99
104
|
<em>Definition</em>
|
|
100
105
|
{:else}
|
|
101
|
-
<Format
|
|
106
|
+
<Format
|
|
107
|
+
node={Parser.parseFormat(
|
|
108
|
+
undefined,
|
|
109
|
+
definition.definition
|
|
110
|
+
).withTextIfEmpty()}
|
|
111
|
+
/>
|
|
102
112
|
{/if}
|
|
103
113
|
{#if edition && editable}
|
|
104
114
|
<span bind:this={synonymsEditor}>
|
|
@@ -110,7 +120,7 @@ $: syns = definition.synonyms || [];
|
|
|
110
120
|
<TextEditor
|
|
111
121
|
text={syn}
|
|
112
122
|
label={'Synonym editor.'}
|
|
113
|
-
placeholder="
|
|
123
|
+
placeholder="synonym"
|
|
114
124
|
valid={() => undefined}
|
|
115
125
|
save={(text) => {
|
|
116
126
|
const newSyns = [...syns];
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
<script>import Parser from '../../models/chapter/Parser';
|
|
2
2
|
import ChapterBody from '../chapter/ChapterBody.svelte';
|
|
3
3
|
import BookishEditor from '../editor/BookishEditor.svelte';
|
|
4
|
-
import { getEdition,
|
|
5
|
-
let
|
|
4
|
+
import { getAuth, getEdition, getLeasee, isEditionEditable, lease, } from './Contexts';
|
|
5
|
+
let auth = getAuth();
|
|
6
|
+
let editable = isEditionEditable();
|
|
6
7
|
let edition = getEdition();
|
|
7
|
-
$: descriptionNode = $edition
|
|
8
|
-
? Parser.parseChapter($edition, $edition.getDescription())
|
|
9
|
-
: undefined;
|
|
10
8
|
</script>
|
|
11
9
|
|
|
12
|
-
{#if
|
|
10
|
+
{#if $edition}
|
|
13
11
|
<section class="bookish-description">
|
|
14
12
|
{#if editable}
|
|
15
13
|
<BookishEditor
|
|
16
|
-
|
|
14
|
+
text={$edition.getDescription()}
|
|
15
|
+
parser={(text) => Parser.parseChapter($edition, text)}
|
|
17
16
|
save={(node) =>
|
|
18
17
|
$edition
|
|
19
18
|
? edition.set(
|
|
@@ -22,10 +21,14 @@ $: descriptionNode = $edition
|
|
|
22
21
|
: undefined}
|
|
23
22
|
chapter={false}
|
|
24
23
|
component={ChapterBody}
|
|
25
|
-
placeholder="
|
|
24
|
+
placeholder="book description"
|
|
25
|
+
leasee={getLeasee(auth, edition, `description`)}
|
|
26
|
+
lease={(lock) => lease(auth, edition, `description`, lock)}
|
|
26
27
|
/>
|
|
27
28
|
{:else}
|
|
28
|
-
<ChapterBody
|
|
29
|
+
<ChapterBody
|
|
30
|
+
node={Parser.parseChapter($edition, $edition.getDescription())}
|
|
31
|
+
/>
|
|
29
32
|
{/if}
|
|
30
33
|
</section>
|
|
31
34
|
{/if}
|
|
@@ -2,14 +2,10 @@
|
|
|
2
2
|
import { goto } from '$app/navigation';
|
|
3
3
|
import { onMount, setContext } from 'svelte';
|
|
4
4
|
import { writable } from 'svelte/store';
|
|
5
|
-
import { BASE, DARK_MODE,
|
|
5
|
+
import { BASE, DARK_MODE, } from './Contexts';
|
|
6
6
|
import { BookishTheme } from '../../models/book/Theme';
|
|
7
7
|
// Poly fill smooth scrolling for Safari.
|
|
8
8
|
onMount(() => smoothscroll.polyfill());
|
|
9
|
-
// True if the edition should be editable. Distributed as context.
|
|
10
|
-
export let editable = false;
|
|
11
|
-
// When editable changes, update the context.
|
|
12
|
-
$: setContext(EDITABLE, editable);
|
|
13
9
|
// The base path allows links to adjust to different routing contexts in which a book is placed.
|
|
14
10
|
// For example, when the book is hosted alone, all routes might start with the bare root "/",
|
|
15
11
|
// but when the book is being viewed or edited in the Bookish app, it needs a prefix for the
|
|
@@ -32,18 +28,24 @@ let darkMode = writable(typeof localStorage !== 'undefined' &&
|
|
|
32
28
|
setContext(DARK_MODE, darkMode);
|
|
33
29
|
// When dark mode changes, update the body's class list.
|
|
34
30
|
$: {
|
|
35
|
-
if (
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
31
|
+
if (typeof document !== 'undefined') {
|
|
32
|
+
if ($darkMode)
|
|
33
|
+
document.body.classList.add('dark');
|
|
34
|
+
else
|
|
35
|
+
document.body.classList.remove('dark');
|
|
36
|
+
if (typeof localStorage !== 'undefined')
|
|
37
|
+
localStorage.setItem('dark', $darkMode ? 'true' : 'false');
|
|
38
|
+
// Set the theme, whatever it is, and change it when the edition changes
|
|
39
|
+
setTheme(edition.getTheme() ?? BookishTheme);
|
|
40
|
+
}
|
|
41
41
|
}
|
|
42
|
+
// Set the theme on mount.
|
|
43
|
+
onMount(() => {
|
|
44
|
+
setTheme(edition.getTheme() ?? BookishTheme);
|
|
45
|
+
});
|
|
42
46
|
// Redirect old hash routes by simply replacing their hash before routing.
|
|
43
47
|
if (typeof window !== 'undefined' && window.location.hash.startsWith('#/'))
|
|
44
48
|
goto(location.hash.replace('#', ''));
|
|
45
|
-
// Set the theme, whatever it is, and change it when the edition changes.
|
|
46
|
-
$: setTheme(edition.getTheme() ?? BookishTheme);
|
|
47
49
|
/** Given a theme, sets the appropriate CSS rules in the browser to apply the theme.*/
|
|
48
50
|
function setTheme(theme) {
|
|
49
51
|
// If the theme is being unset, make sure we've removed any overrding style declaration.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<script>import Loading from './Loading.svelte';
|
|
2
|
+
import Edition from './Edition.svelte';
|
|
3
|
+
import Feedback from '../app/Feedback.svelte';
|
|
4
|
+
import { getBook, getEdition, getAuth, CHAPTERTEXT, getChapterText, } from './Contexts';
|
|
5
|
+
import { onDestroy } from 'svelte';
|
|
6
|
+
import { page } from '$app/stores';
|
|
7
|
+
import { listenToChapters, listenToEdition } from '../../models/CRUD';
|
|
8
|
+
export let write;
|
|
9
|
+
let auth = getAuth();
|
|
10
|
+
let book = getBook();
|
|
11
|
+
let edition = getEdition();
|
|
12
|
+
let chapterText = getChapterText();
|
|
13
|
+
let editionUnsub = undefined;
|
|
14
|
+
let chaptersUnsub = undefined;
|
|
15
|
+
let error = undefined;
|
|
16
|
+
function unsub() {
|
|
17
|
+
// Unsubscribe to the old edition document if we were listening to one.
|
|
18
|
+
if (editionUnsub)
|
|
19
|
+
editionUnsub();
|
|
20
|
+
if (chaptersUnsub)
|
|
21
|
+
chaptersUnsub();
|
|
22
|
+
}
|
|
23
|
+
function listen() {
|
|
24
|
+
unsub();
|
|
25
|
+
error = undefined;
|
|
26
|
+
// If we no longer have a book, we can't get an edition.
|
|
27
|
+
if ($book === undefined) {
|
|
28
|
+
edition.set(undefined);
|
|
29
|
+
error = 'Unknown book.';
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
const editionNumber = $page.params.editionid;
|
|
34
|
+
const latestPublished = !write;
|
|
35
|
+
// Figure out which edition to load.
|
|
36
|
+
let editionID;
|
|
37
|
+
if (editionNumber === undefined) {
|
|
38
|
+
if (latestPublished) {
|
|
39
|
+
editionID = $book.getLatestPublishedEditionID();
|
|
40
|
+
if (editionID === undefined)
|
|
41
|
+
error = 'There is no published edition of this book.';
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
editionID = $book.getLatestEditionID();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
editionID = $book.getEditionNumberID(parseFloat(editionNumber));
|
|
49
|
+
if (editionID === undefined)
|
|
50
|
+
error = `There is no ${editionNumber} edition of this book`;
|
|
51
|
+
}
|
|
52
|
+
// Listen to the doc for changes and listen to all of its chapters.
|
|
53
|
+
if (editionID !== undefined) {
|
|
54
|
+
editionUnsub = listenToEdition($book.ref.id, editionID, (ed) => {
|
|
55
|
+
if (ed) {
|
|
56
|
+
// Before setting the new edition, augmented it with the chapter text.
|
|
57
|
+
edition.set($chapterText
|
|
58
|
+
? ed.withChapterText($chapterText)
|
|
59
|
+
: ed);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
edition.set(undefined);
|
|
63
|
+
error = 'Unable to load edition';
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
chaptersUnsub = listenToChapters($book.ref.id, editionID, (chapters) => {
|
|
67
|
+
// Update the chapter text store.
|
|
68
|
+
chapterText.set(chapters);
|
|
69
|
+
// Update the chapter text in the current edition.
|
|
70
|
+
if ($edition)
|
|
71
|
+
edition.set($edition.withChapterText(chapters));
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Whenever the book or page changes, change the listener.
|
|
77
|
+
$: if ($page && $auth && $auth.user !== null)
|
|
78
|
+
listen();
|
|
79
|
+
// When unmounted, unset the stores — no longer viewing a book.
|
|
80
|
+
onDestroy(() => {
|
|
81
|
+
edition.set(undefined);
|
|
82
|
+
unsub();
|
|
83
|
+
});
|
|
84
|
+
</script>
|
|
85
|
+
|
|
86
|
+
{#if $book === undefined || error}
|
|
87
|
+
<Feedback error>Unable to load edition.</Feedback>
|
|
88
|
+
{:else if $edition === undefined}
|
|
89
|
+
<Loading />
|
|
90
|
+
{:else}
|
|
91
|
+
<Edition
|
|
92
|
+
edition={$edition}
|
|
93
|
+
base={write
|
|
94
|
+
? `/write/${$book.getRefID()}/${$edition.getEditionNumber()}/`
|
|
95
|
+
: `/${$book.getSubdomain() ?? $book.getRefID()}/`}
|
|
96
|
+
>
|
|
97
|
+
<slot />
|
|
98
|
+
</Edition>
|
|
99
|
+
{/if}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { SvelteComponentTyped } from "svelte";
|
|
2
|
+
declare const __propDef: {
|
|
3
|
+
props: {
|
|
4
|
+
write: boolean;
|
|
5
|
+
};
|
|
6
|
+
events: {
|
|
7
|
+
[evt: string]: CustomEvent<any>;
|
|
8
|
+
};
|
|
9
|
+
slots: {
|
|
10
|
+
default: {};
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
export type EditionLoaderProps = typeof __propDef.props;
|
|
14
|
+
export type EditionLoaderEvents = typeof __propDef.events;
|
|
15
|
+
export type EditionLoaderSlots = typeof __propDef.slots;
|
|
16
|
+
export default class EditionLoader extends SvelteComponentTyped<EditionLoaderProps, EditionLoaderEvents, EditionLoaderSlots> {
|
|
17
|
+
}
|
|
18
|
+
export {};
|