Skip to content

Commit

Permalink
Merge pull request #980 from npocccties/lti-deep-link
Browse files Browse the repository at this point in the history
feat: LTI Deep Linking 2.0
  • Loading branch information
ties-makimura authored Sep 14, 2023
2 parents ddfc45d + 1d144f8 commit 78460f9
Show file tree
Hide file tree
Showing 73 changed files with 3,128 additions and 679 deletions.
4 changes: 3 additions & 1 deletion components/atoms/AuthorFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@ type Props = {
sx?: SxProps;
disabled?: boolean;
onFilterChange?: (value: AuthorFilterType) => void;
row?: boolean | undefined;
};

function AuthorFilter({
value = options[0].value,
sx,
disabled = false,
onFilterChange,
row,
}: Props) {
const handleChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
Expand All @@ -38,7 +40,7 @@ function AuthorFilter({
return (
<FormControl component="fieldset" sx={sx}>
<FormLabel component="legend">作成者</FormLabel>
<RadioGroup value={value} onChange={handleChange}>
<RadioGroup value={value} onChange={handleChange} row={row}>
{options.map(({ value, label }) => (
<FormControlLabel
key={value}
Expand Down
4 changes: 3 additions & 1 deletion components/atoms/SharedFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@ type Props = {
sx?: SxProps;
disabled?: boolean;
onFilterChange?: (value: SharedFilterType) => void;
row?: boolean | undefined;
};

function SharedFilter({
value = options[2].value,
sx,
disabled = false,
onFilterChange,
row,
}: Props) {
const handleChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
Expand All @@ -38,7 +40,7 @@ function SharedFilter({
return (
<FormControl component="fieldset" sx={sx}>
<FormLabel component="legend">共有</FormLabel>
<RadioGroup value={value} onChange={handleChange}>
<RadioGroup value={value} onChange={handleChange} row={row}>
{options.map(({ value, label }) => (
<FormControlLabel
key={value}
Expand Down
120 changes: 63 additions & 57 deletions components/organisms/AppBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const useStyles = makeStyles((theme) => ({
inner: {
display: "flex",
alignItems: "center",
justifyContent: "space-between",
maxWidth: theme.breakpoints.values.lg,
width: "100%",
margin: "0 auto",
Expand Down Expand Up @@ -88,6 +89,7 @@ function AppBar(props: Props, ref: Ref<HTMLDivElement>) {
onDashboardClick,
...others
} = props;
const isDeepLink = !!session.ltiDlSettings?.deep_link_return_url;
const appBarClasses = useAppBarStyles();
const classes = useStyles();
const [open, setOpen] = useState(false);
Expand Down Expand Up @@ -137,71 +139,75 @@ function AppBar(props: Props, ref: Ref<HTMLDivElement>) {
alt="CHiBi-CHiLO"
className={clsx(classes.margin, classes.logo)}
/>
<div className={classes.nav}>
<AppBarNavButton
color="inherit"
icon={<MenuBookOutlinedIcon />}
label="ブック"
onClick={onBooksClick}
disabled={!onBooksClick}
/>
<AppBarNavButton
color="inherit"
icon={<LibraryBooksOutlinedIcon />}
label="トピック"
onClick={onTopicsClick}
disabled={!onTopicsClick}
/>
<AppBarNavButton
color="inherit"
icon={<LinkIcon />}
label="リンク"
onClick={onCoursesClick}
disabled={!onCoursesClick}
/>
<AppBarNavButton
color="inherit"
icon={<CellTowerIcon />}
label="配信中のブック"
onClick={onBookClick}
disabled={
!onBookClick ||
!Number.isFinite(session?.ltiResourceLink?.bookId)
}
/>
{session?.systemSettings?.zoomImportEnabled && ( // TODO: zoomインポート以外の設定値が実装されたら常時表示する
{!isDeepLink && (
<div className={classes.nav}>
<AppBarNavButton
color="inherit"
icon={<SettingsIcon />}
label="設定"
onClick={handleOpenUserSettings}
icon={<MenuBookOutlinedIcon />}
label="ブック"
onClick={onBooksClick}
disabled={!onBooksClick}
/>
)}
{onDashboardClick && (
<AppBarNavButton
color="inherit"
icon={<AssessmentOutlinedIcon />}
label="学習分析"
onClick={onDashboardClick}
icon={<LibraryBooksOutlinedIcon />}
label="トピック"
onClick={onTopicsClick}
disabled={!onTopicsClick}
/>
)}
</div>
<div className={clsx(classes.user, classes.margin)}>
<p>{session.user.name}</p>
<p className={classes.roles}>{role(session)}</p>
</div>
{session && (
<>
<Button variant="text" color="primary" onClick={handleClick}>
LTI情報
</Button>
<LtiItemDialog
open={open}
onClose={handleClose}
session={session}
<AppBarNavButton
color="inherit"
icon={<LinkIcon />}
label="リンク"
onClick={onCoursesClick}
disabled={!onCoursesClick}
/>
</>
<AppBarNavButton
color="inherit"
icon={<CellTowerIcon />}
label="配信中のブック"
onClick={onBookClick}
disabled={
!onBookClick ||
!Number.isFinite(session?.ltiResourceLink?.bookId)
}
/>
{session?.systemSettings?.zoomImportEnabled && ( // TODO: zoomインポート以外の設定値が実装されたら常時表示する
<AppBarNavButton
color="inherit"
icon={<SettingsIcon />}
label="設定"
onClick={handleOpenUserSettings}
/>
)}
{onDashboardClick && (
<AppBarNavButton
color="inherit"
icon={<AssessmentOutlinedIcon />}
label="学習分析"
onClick={onDashboardClick}
/>
)}
</div>
)}
<div>
<div className={clsx(classes.user, classes.margin)}>
<p>{session.user.name}</p>
<p className={classes.roles}>{role(session)}</p>
</div>
{session && (
<>
<Button variant="text" color="primary" onClick={handleClick}>
LTI情報
</Button>
<LtiItemDialog
open={open}
onClose={handleClose}
session={session}
/>
</>
)}
</div>
</div>
</Toolbar>
<Snackbar
Expand Down
36 changes: 24 additions & 12 deletions components/organisms/ContentPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ type Props = Parameters<typeof Checkbox>[0] & {
onKeywordClick(keyword: Pick<KeywordSchema, "name">): void;
onRelatedBookClick?(id: RelatedBook): void;
linked?: boolean;
isDeepLink?: boolean;
};

export default function ContentPreview({
Expand All @@ -135,6 +136,7 @@ export default function ContentPreview({
onKeywordClick,
onRelatedBookClick,
linked = content.type === "book" ? false : undefined,
isDeepLink,
checked,
...checkboxProps
}: Props) {
Expand Down Expand Up @@ -177,7 +179,7 @@ export default function ContentPreview({
/>
))}
{content.shared && <SharedIndicator className="shared" />}
{onContentEditClick && (
{!isDeepLink && onContentEditClick && (
<EditButton
className="edit-button"
variant={content.type}
Expand All @@ -198,17 +200,27 @@ export default function ContentPreview({
/>
</CardActionArea>
<Box position="relative">
{linked !== undefined && onContentLinkClick && (
<LinkSwitch
sx={{
position: "absolute",
bottom: 0,
right: 0,
transform: "translateY(50%)",
}}
checked={linked}
onChange={handleContentLinkClick}
/>
{content.type === "book" && linked !== undefined && (
<label
title={
onContentLinkClick
? "リンクを切り替える"
: "ツールURLが指定されているため、リンクの切り替えはできません"
}
>
<LinkSwitch
sx={{
position: "absolute",
bottom: 0,
right: 0,
transform: "translateY(50%)",
filter: onContentLinkClick ? "none" : "grayscale(1)",
}}
disabled={!onContentLinkClick}
checked={linked}
onChange={handleContentLinkClick}
/>
</label>
)}
{content.license && (
<License
Expand Down
4 changes: 3 additions & 1 deletion components/templates/BookEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type { TopicSchema } from "$server/models/topic";
import type { AuthorSchema } from "$server/models/author";
import type { IsContentEditable } from "$server/models/content";
import { useConfirm } from "material-ui-confirm";
import { useSessionAtom } from "$store/session";
import useDialogProps from "$utils/useDialogProps";

const useStyles = makeStyles((theme) => ({
Expand Down Expand Up @@ -73,6 +74,7 @@ export default function BookEdit({
isContentEditable,
linked = false,
}: Props) {
const { session } = useSessionAtom();
const classes = useStyles();
const confirm = useConfirm();
const {
Expand Down Expand Up @@ -121,7 +123,7 @@ export default function BookEdit({
<BookForm
className={classes.content}
book={book}
linked={linked}
linked={Boolean(session?.ltiTargetLinkUri || linked)}
variant="update"
onSubmit={onSubmit}
onAuthorsUpdate={onAuthorsUpdate}
Expand Down
3 changes: 2 additions & 1 deletion components/templates/BookNew.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export default function BookNew({
onAuthorsUpdate,
onAuthorSubmit,
}: Props) {
const { isContentEditable } = useSessionAtom();
const { session, isContentEditable } = useSessionAtom();
const forkFrom =
book && !isContentEditable(book) && book.authors.length > 0 && book.authors;
const defaultBook = book && {
Expand Down Expand Up @@ -87,6 +87,7 @@ export default function BookNew({
<BookForm
book={defaultBook}
topics={topics}
linked={Boolean(session?.ltiTargetLinkUri)}
variant="create"
onSubmit={onSubmit}
onAuthorsUpdate={onAuthorsUpdate}
Expand Down
2 changes: 1 addition & 1 deletion components/templates/Books.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export type Props = {
loading?: boolean;
onContentPreviewClick(content: ContentSchema): void;
onContentEditClick(content: ContentSchema): void;
onContentLinkClick(content: ContentSchema, checked: boolean): void;
onContentLinkClick?(content: ContentSchema, checked: boolean): void;
onLinkedBookClick?(book: BookSchema): void;
onBookNewClick(): void;
onBooksImportClick(): void;
Expand Down
28 changes: 28 additions & 0 deletions components/templates/DeepLInkBooks.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { Story } from "@storybook/react";
import DeepLinkBooks from "./DeepLinkBooks";
import { book, books } from "samples";

export default {
title: "templates/DeepLinkBooks",
parameters: { layout: "fullscreen" },
component: DeepLinkBooks,
};

const linkedBook = { ...book, editable: true };

const Template: Story<Parameters<typeof DeepLinkBooks>[0]> = (args) => (
<DeepLinkBooks {...args} />
);

export const Default = Template.bind({});
Default.args = {
linkedBook,
totalCount: 123,
contents: books.map((book) => ({ type: "book", ...book })),
};

export const Empty = Template.bind({});
Empty.args = {
totalCount: 0,
contents: [],
};
Loading

0 comments on commit 78460f9

Please sign in to comment.