ability to share phrase url

This commit is contained in:
lingdocs 2022-05-03 10:23:33 -05:00
parent 24e85f9df6
commit b75ddb9689
14 changed files with 118 additions and 28 deletions

View File

@ -1,6 +1,6 @@
{
"name": "@lingdocs/pashto-inflector",
"version": "2.3.7",
"version": "2.3.8",
"author": "lingdocs.com",
"description": "A Pashto inflection and verb conjugation engine, inculding React components for displaying Pashto text, inflections, and conjugations",
"homepage": "https://verbs.lingdocs.com",
@ -24,6 +24,8 @@
},
"dependencies": {
"classnames": "^2.2.6",
"jsurl2": "^2.1.0",
"lz-string": "^1.4.4",
"pbf": "^3.2.1",
"rambda": "^6.7.0",
"react-select": "^5.2.2"

View File

@ -2,11 +2,13 @@ import * as T from "../../types";
import { renderEP } from "../../lib/phrase-building/render-ep";
import { compileEP } from "../../lib/phrase-building/compile-ep";
import AbbreviationFormSelector from "../vp-explorer/AbbreviationFormSelector";
import useStickyState from "../../lib/useStickyState";
import Examples from "../Examples";
function EPDisplay({ eps, opts }: { eps: T.EPSelectionState, opts: T.TextOptions }) {
const [form, setForm] = useStickyState<T.FormVersion>({ removeKing: false, shrinkServant: false }, "abbreviationFormEq");
function EPDisplay({ eps, opts, setForm }: {
eps: T.EPSelectionState,
opts: T.TextOptions,
setForm: (form: T.FormVersion) => void,
}) {
const EP = completeEPSelection(eps);
if (!EP) {
return <div className="lead text-center my-4">
@ -20,11 +22,11 @@ function EPDisplay({ eps, opts }: { eps: T.EPSelectionState, opts: T.TextOptions
</div>
}
const rendered = renderEP(EP);
const result = compileEP(rendered, form);
const result = compileEP(rendered, EP.form);
return <div className="text-center pt-3">
<AbbreviationFormSelector
adjustable="king"
form={{ ...form, shrinkServant: false }}
form={EP.form}
onChange={setForm}
/>
{"long" in result.ps ?

View File

@ -32,7 +32,8 @@ function EPExplorer(props: {
negative: false,
},
shrunkenPossesive: undefined,
}, "EPSelectionState3");
form: { removeKing: false, shrinkServant: false },
}, "EPSelectionState10");
function handlePredicateTypeChange(type: "NP" | "Complement") {
setEps(o => ({
...o,
@ -63,6 +64,12 @@ function EPExplorer(props: {
shrunkenPossesive,
}));
}
function handleSetForm(form: T.FormVersion) {
setEps(o => ({
...o,
form,
}));
}
const king = eps.subject?.type === "pronoun"
? "subject"
: eps.predicate.type === "Complement"
@ -134,7 +141,11 @@ function EPExplorer(props: {
</div>
</div>
{mode === "charts" && <EqChartsDisplay tense={eps.equative.tense} opts={props.opts} />}
{mode === "phrases" && <EPDisplay opts={props.opts} eps={eps} />}
{mode === "phrases" && <EPDisplay
opts={props.opts}
eps={eps}
setForm={handleSetForm}
/>}
</div>;
}

View File

@ -30,7 +30,6 @@ function NPPicker(props: {
const [npType, setNpType] = useState<T.NPType | undefined>(props.np ? props.np.type : undefined);
useEffect(() => {
setNpType(props.np ? props.np.type : undefined);
// setAddingPoss(false);
}, [props.np]);
function handleClear() {
if (props.np && props.np.type === "noun" && props.np.dynamicComplement) return;

View File

@ -8,8 +8,11 @@ import {
import useStickyState from "../../lib/useStickyState";
import Examples from "../Examples";
function VPDisplay({ VP, opts }: { VP: T.VPSelectionState, opts: T.TextOptions }) {
const [form, setForm] = useStickyState<T.FormVersion>({ removeKing: false, shrinkServant: false }, "abbreviationForm");
function VPDisplay({ VP, opts, setForm }: {
VP: T.VPSelectionState,
opts: T.TextOptions,
setForm: (form: T.FormVersion) => void,
}) {
const [OSV, setOSV] = useStickyState<boolean>(false, "includeOSV");
const VPComplete = completeVPSelection(VP);
if (!VPComplete) {
@ -20,7 +23,7 @@ function VPDisplay({ VP, opts }: { VP: T.VPSelectionState, opts: T.TextOptions }
})()}
</div>;
}
const result = compileVP(renderVP(VPComplete), { ...form, OSV });
const result = compileVP(renderVP(VPComplete), { ...VP.form, OSV });
return <div className="text-center mt-1">
{VP.verb.transitivity === "transitive" && <div className="form-check mb-2">
<input
@ -36,7 +39,7 @@ function VPDisplay({ VP, opts }: { VP: T.VPSelectionState, opts: T.TextOptions }
</div>}
<AbbreviationFormSelector
adjustable={whatsAdjustable(VPComplete)}
form={form}
form={VP.form}
onChange={setForm}
/>
{"long" in result.ps ?

View File

@ -16,17 +16,11 @@ import { isPastTense } from "../../lib/phrase-building/vp-tools";
import VPExplorerQuiz from "./VPExplorerQuiz";
import { switchSubjObj } from "../../lib/phrase-building/vp-tools";
import VPExplorerExplanationModal, { roleIcon } from "./VPExplorerExplanationModal";
// @ts-ignore
import LZString from "lz-string";
// TO FINISH IMPERATIVE STUFF!!
// TODO: English Builders for imperatives
// TODO: Quiz with imperatives
const phraseURLParam = "VPPhrase";
// TODO: make answerFeedback emojis appear at random translate angles a little bit
// add energy drinks?
// TODO: Drill Down text display options
// TODO: SHOW KING AND SERVANT ONCE TENSE PICKED, EVEN IF NPs not selected
// TODO: Issue with dynamic compounds english making with plurals
// TODO: Issue with "the money were taken"
// TODO: Use the same component for PronounPicker and NPPronounPicker (sizing issue)
@ -38,14 +32,17 @@ import VPExplorerExplanationModal, { roleIcon } from "./VPExplorerExplanationMod
// TODO: error handling on error with rendering etc
export function VPExplorer(props: {
loaded?: T.VPSelectionState,
verb: T.VerbEntry,
opts: T.TextOptions,
handleLinkClick: ((ts: number) => void) | "none",
entryFeeder: T.EntryFeeder,
}) {
const [vps, setVps] = useStickyState<T.VPSelectionState>(
savedVps => makeVPSelectionState(props.verb, savedVps),
"vpsState7",
props.loaded
? props.loaded
: savedVps => makeVPSelectionState(props.verb, savedVps),
"vpsState8",
);
const [mode, setMode] = useStickyState<"charts" | "phrases" | "quiz">(
savedMode => {
@ -55,12 +52,20 @@ export function VPExplorer(props: {
},
"verbExplorerMode2",
);
const [showShareClipped, setShowShareClipped] = useState<boolean>(false);
const [showingExplanation, setShowingExplanation] = useState<{ role: "servant" | "king", item: "subject" | "object" } | false>(false);
const isPast = isPastTense(vps.verb.tenseCategory === "perfect" ? vps.verb.perfectTense : vps.verb.verbTense);
const roles = getKingAndServant(
isPast,
vps.verb.transitivity !== "intransitive",
);
useEffect(() => {
const VPSFromUrl = getVPSFromUrl();
if (VPSFromUrl) {
setVps(VPSFromUrl);
}
// eslint-disable-next-line
}, []);
useEffect(() => {
setVps(oldVps => {
if (mode === "quiz") {
@ -112,6 +117,21 @@ export function VPExplorer(props: {
}
return f;
}
function handleSetForm(form: T.FormVersion) {
setVps(o => ({
...o,
form,
}));
}
// for some crazy reason I can't get the URI share thing to encode and decode properly
function handleCopyShareLink() {
const shareUrl = getShareUrl(vps);
navigator.clipboard.writeText(shareUrl);
setShowShareClipped(true);
setTimeout(() => {
setShowShareClipped(false);
}, 1000);
}
return <div className="mt-3" style={{ maxWidth: "950px"}}>
<VerbPicker
vps={vps}
@ -119,7 +139,9 @@ export function VPExplorer(props: {
opts={props.opts}
handleLinkClick={props.handleLinkClick}
/>
<div className="mt-2 mb-3 text-center">
<div className="mt-2 mb-3 d-flex flex-row justify-content-between align-items-center">
<div style={{ width: "1rem" }}>
</div>
<ButtonSelect
value={mode}
options={[
@ -129,6 +151,13 @@ export function VPExplorer(props: {
]}
handleChange={setMode}
/>
<div
className="clickable"
onClick={mode === "phrases" ? handleCopyShareLink : undefined}
style={{ width: "1rem" }}
>
{mode === "phrases" ? <i className="fas fa-share-alt" /> : ""}
</div>
</div>
{(vps.verb && (typeof vps.verb.object === "object") && (vps.verb.isCompound !== "dynamic") && (vps.verb.tenseCategory !== "imperative") &&(mode === "phrases")) &&
<div className="text-center my-2">
@ -183,13 +212,26 @@ export function VPExplorer(props: {
/>
</div>
</div>}
{mode === "phrases" && <VPDisplay VP={vps} opts={props.opts} />}
{mode === "phrases" && <VPDisplay
VP={vps}
opts={props.opts}
setForm={handleSetForm}
/>}
{mode === "charts" && <ChartDisplay VS={vps.verb} opts={props.opts} />}
{mode === "quiz" && <VPExplorerQuiz opts={props.opts} vps={vps} />}
<VPExplorerExplanationModal
showing={showingExplanation}
setShowing={setShowingExplanation}
/>
{showShareClipped && <div className="alert alert-primary text-center" role="alert" style={{
position: "fixed",
top: "30%",
left: "50%",
transform: "translate(-50%, -50%)",
zIndex: 9999999999999,
}}>
Phrase URL copied to clipboard
</div>}
</div>
}
@ -202,4 +244,19 @@ function hasPronounConflict(subject: T.NPSelection | undefined, object: undefine
return isInvalidSubjObjCombo(subjPronoun.person, objPronoun.person);
}
function getShareUrl(vps: T.VPSelectionState): string {
const stringJSON = JSON.stringify(vps);
const encoded = LZString.compressToEncodedURIComponent(stringJSON);
const url = new URL(window.location.href);
url.searchParams.append(phraseURLParam, encoded);
return url.toString();
}
function getVPSFromUrl(): T.VPSelectionState | undefined {
const params = new URLSearchParams(window.location.search);
const fromParams = params.get(phraseURLParam);
if (!fromParams) return;
const decoded = LZString.decompressFromEncodedURIComponent(fromParams);
return JSON.parse(decoded) as T.VPSelectionState;
}

View File

@ -446,6 +446,7 @@ function getRandomVPSelection(mix: MixType = "both") {
subject: subject !== undefined ? subject : randSubj,
verb: randomizeTense(verb, true),
shrunkenPossesive: undefined,
form: { removeKing: false, shrinkServant: false },
}
}
const v: T.VerbSelectionComplete = {
@ -462,6 +463,7 @@ function getRandomVPSelection(mix: MixType = "both") {
subject: randSubj,
verb: randomizeTense(v, true),
shrunkenPossesive: undefined,
form: { removeKing: false, shrinkServant: false },
};
};
};

View File

@ -66,6 +66,7 @@ export function makeVPSelectionState(
canChangeVoice: transitivity === "transitive",
canChangeStatDyn: "stative" in info,
},
form: os ? os.form : { removeKing: false, shrinkServant: false },
shrunkenPossesive: os ? os.shrunkenPossesive : undefined,
};
}

View File

@ -35,7 +35,7 @@ export function compileEP(EP: T.EPRendered, form: T.FormVersion, combineLengths?
};
}
function getSegmentsAndKids(EP: T.EPRendered, form: T.FormVersion): { kids: Segment[], NPs: Segment[] } {
function getSegmentsAndKids(EP: T.EPRendered, form: Omit<T.FormVersion, "shrinkServant">): { kids: Segment[], NPs: Segment[] } {
function ifNotRemoved(s: Segment, role: "subject" | "predicate"): Segment[] {
if (form.removeKing && EP.king === role) {
return [];

View File

@ -33,6 +33,7 @@ export function renderEP(EP: T.EPSelectionComplete): T.EPRendered {
equative: renderEquative(EP.equative, kingPerson),
englishBase: equativeBuilders[EP.equative.tense](kingPerson, EP.equative.negative),
shrunkenPossesive: EP.shrunkenPossesive,
form: EP.form,
};
}

View File

@ -59,6 +59,7 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
object: VP.verb.isCompound === "dynamic" ? "none" : VP.verb.object,
vs: VP.verb,
}),
form: VP.form,
};
return b;
}

File diff suppressed because one or more lines are too long

View File

@ -509,6 +509,7 @@ export type VPRendered = {
object: Rendered<NPSelection> | ObjectNP,
verb: VerbRendered,
englishBase?: string[],
form: FormVersion,
}
export type VerbTense = "presentVerb"
@ -531,12 +532,14 @@ export type VPSelectionState = {
subject: NPSelection | undefined,
verb: VerbSelection,
shrunkenPossesive: undefined | number,
form: FormVersion,
};
export type VPSelectionComplete = {
subject: NPSelection,
verb: VerbSelectionComplete,
shrunkenPossesive: undefined | number,
form: FormVersion,
};
export type VerbSelectionComplete = Omit<VerbSelection, "object" | "verbTense" | "perfectTense" | "imperfectiveTense" | "tenseCategory"> & {
@ -664,6 +667,7 @@ export type EPSelectionState = {
},
equative: EquativeSelection,
shrunkenPossesive: undefined | number,
form: FormVersion,
};
export type EPSelectionComplete = Omit<EPSelectionState, "subject" | "predicate"> & {
@ -675,6 +679,7 @@ export type EPSelectionComplete = Omit<EPSelectionState, "subject" | "predicate"
type: "Complement",
selection: EqCompSelection,
},
form: FormVersion,
};
export type EqCompType = "adjective" | "loc. adv."; // TODO: - more
@ -699,6 +704,7 @@ export type EPRendered = {
equative: EquativeRendered,
englishBase?: string[],
shrunkenPossesive: undefined | number,
form: FormVersion,
}
export type EntryFeeder = {

View File

@ -7131,6 +7131,11 @@ jsonfile@^6.0.1:
optionalDependencies:
graceful-fs "^4.1.6"
jsurl2@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/jsurl2/-/jsurl2-2.1.0.tgz#4c7f0cd33344f318cea5e321089c983194e947c7"
integrity sha512-dN5xpdjYWxhkn/hN9jGovVP/sy8HqP3BiNU7nKm7pwtRSfQlKG8FZO7wlO4kmydAYzv3UwVVzGjAnEBr4KCmmw==
"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.1.0:
version "3.2.1"
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.2.1.tgz#720b97bfe7d901b927d87c3773637ae8ea48781b"