Skip to content

Commit

Permalink
Extract ShareAnnotations component
Browse files Browse the repository at this point in the history
  • Loading branch information
lyzadanger committed Jul 26, 2023
1 parent b1ddafb commit c0eb4f6
Show file tree
Hide file tree
Showing 4 changed files with 325 additions and 304 deletions.
115 changes: 115 additions & 0 deletions src/sidebar/components/ShareDialog/ShareAnnotations.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import {
CopyIcon,
Input,
InputGroup,
IconButton,
LockIcon,
} from '@hypothesis/frontend-shared';
import { useCallback } from 'preact/hooks';

import { pageSharingLink } from '../../helpers/annotation-sharing';
import { withServices } from '../../service-context';
import type { ToastMessengerService } from '../../services/toast-messenger';
import { useSidebarStore } from '../../store';
import { copyText } from '../../util/copy-to-clipboard';
import ShareLinks from '../ShareLinks';
import LoadingSpinner from './LoadingSpinner';

export type ShareAnnotationsProps = {
// injected
toastMessenger: ToastMessengerService;
};

/**
* Render UI for sharing annotations (by URL) within the currently-focused group
*/
function ShareAnnotations({ toastMessenger }: ShareAnnotationsProps) {
const store = useSidebarStore();
const mainFrame = store.mainFrame();
const focusedGroup = store.focusedGroup();
const sharingReady = focusedGroup && mainFrame;

const shareURI =
sharingReady && pageSharingLink(mainFrame.uri, focusedGroup.id);

const copyShareLink = useCallback(() => {
try {
if (shareURI) {
copyText(shareURI);
toastMessenger.success('Copied share link to clipboard');
}
} catch (err) {
toastMessenger.error('Unable to copy link');
}
}, [shareURI, toastMessenger]);

if (!sharingReady) {
return <LoadingSpinner />;
}

return (
<div className="text-color-text-light space-y-3">
{shareURI ? (
<>
<div
className="text-color-text font-medium"
data-testid="sharing-intro"
>
{focusedGroup.type === 'private' ? (
<p>
Use this link to share these annotations with other group
members:
</p>
) : (
<p>Use this link to share these annotations with anyone:</p>
)}
</div>
<div>
<InputGroup>
<Input
aria-label="Use this URL to share these annotations"
type="text"
value={shareURI}
readOnly
/>
<IconButton
icon={CopyIcon}
onClick={copyShareLink}
title="Copy share link"
variant="dark"
/>
</InputGroup>
</div>
<p data-testid="sharing-details">
{focusedGroup.type === 'private' ? (
<span>
Annotations in the private group <em>{focusedGroup.name}</em>{' '}
are only visible to group members.
</span>
) : (
<span>
Anyone using this link may view the annotations in the group{' '}
<em>{focusedGroup.name}</em>.
</span>
)}{' '}
<span>
Private (
<LockIcon className="inline w-em h-em ml-0.5 -mt-0.5" />{' '}
<em>Only Me</em>) annotations are only visible to you.
</span>
</p>
<div className="text-[24px]">
<ShareLinks shareURI={shareURI} />
</div>
</>
) : (
<p data-testid="no-sharing">
These annotations cannot be shared because this document is not
available on the web.
</p>
)}
</div>
);
}

export default withServices(ShareAnnotations, ['toastMessenger']);
164 changes: 10 additions & 154 deletions src/sidebar/components/ShareDialog/ShareDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,156 +1,28 @@
import {
Card,
CopyIcon,
IconButton,
Input,
InputGroup,
LockIcon,
Tab,
} from '@hypothesis/frontend-shared';
import { useCallback, useState } from 'preact/hooks';
import { Card, Tab } from '@hypothesis/frontend-shared';
import { useState } from 'preact/hooks';

import { pageSharingLink } from '../../helpers/annotation-sharing';
import { withServices } from '../../service-context';
import type { ToastMessengerService } from '../../services/toast-messenger';
import { useSidebarStore } from '../../store';
import { copyText } from '../../util/copy-to-clipboard';
import ShareLinks from '../ShareLinks';
import SidebarPanel from '../SidebarPanel';
import ExportAnnotations from './ExportAnnotations';
import LoadingSpinner from './LoadingSpinner';
import ShareAnnotations from './ShareAnnotations';
import TabHeader from './TabHeader';
import TabPanel from './TabPanel';

type SharePanelContentProps = {
loading: boolean;
shareURI?: string | null;
/** Callback for when "copy URL" button is clicked */
onCopyShareLink: () => void;
groupName?: string;
groupType?: string;
};

/**
* Render content for "share" panel or tab.
* Panel with sharing options.
* - If export feature flag is enabled, will show a tabbed interface with
* share and export tabs
* - Else, shows a single "Share annotations" interface
*/
function SharePanelContent({
groupName,
groupType,
loading,
onCopyShareLink,
shareURI,
}: SharePanelContentProps) {
if (loading) {
return <LoadingSpinner />;
}

return (
<div className="text-color-text-light space-y-3">
{shareURI ? (
<>
<div
className="text-color-text font-medium"
data-testid="sharing-intro"
>
{groupType === 'private' ? (
<p>
Use this link to share these annotations with other group
members:
</p>
) : (
<p>Use this link to share these annotations with anyone:</p>
)}
</div>
<div>
<InputGroup>
<Input
aria-label="Use this URL to share these annotations"
type="text"
value={shareURI}
readOnly
/>
<IconButton
icon={CopyIcon}
onClick={onCopyShareLink}
title="Copy share link"
variant="dark"
/>
</InputGroup>
</div>
<p data-testid="sharing-details">
{groupType === 'private' ? (
<span>
Annotations in the private group <em>{groupName}</em> are only
visible to group members.
</span>
) : (
<span>
Anyone using this link may view the annotations in the group{' '}
<em>{groupName}</em>.
</span>
)}{' '}
<span>
Private (
<LockIcon className="inline w-em h-em ml-0.5 -mt-0.5" />{' '}
<em>Only Me</em>) annotations are only visible to you.
</span>
</p>
<div className="text-[24px]">
<ShareLinks shareURI={shareURI} />
</div>
</>
) : (
<p data-testid="no-sharing">
These annotations cannot be shared because this document is not
available on the web.
</p>
)}
</div>
);
}

export type ShareDialogProps = {
// injected
toastMessenger: ToastMessengerService;
};

/**
* A panel for sharing the current group's annotations on the current document.
*
* Links within this component allow a user to share the set of annotations that
* are on the current page (as defined by the main frame's URI) and contained
* within the app's currently-focused group.
*/
function ShareDialog({ toastMessenger }: ShareDialogProps) {
export default function ShareDialog() {
const store = useSidebarStore();
const mainFrame = store.mainFrame();
const focusedGroup = store.focusedGroup();
const groupName = (focusedGroup && focusedGroup.name) || '...';
const panelTitle = `Share Annotations in ${groupName}`;

const tabbedDialog = store.isFeatureEnabled('export_annotations');
const [selectedTab, setSelectedTab] = useState<'share' | 'export'>('share');

// To be able to concoct a sharing link, a focused group and frame need to
// be available
const sharingReady = focusedGroup && mainFrame;
// Show a loading spinner in the export tab if annotations are loading

const shareURI =
sharingReady && pageSharingLink(mainFrame.uri, focusedGroup.id);

// TODO: Move into Share-panel-content component once extracted
const copyShareLink = useCallback(() => {
try {
if (shareURI) {
copyText(shareURI);
toastMessenger.success('Copied share link to clipboard');
}
} catch (err) {
toastMessenger.error('Unable to copy link');
}
}, [shareURI, toastMessenger]);

return (
<SidebarPanel
title={panelTitle}
Expand Down Expand Up @@ -188,13 +60,7 @@ function ShareDialog({ toastMessenger }: ShareDialogProps) {
aria-labelledby="share-panel-tab"
title={panelTitle}
>
<SharePanelContent
groupName={focusedGroup?.name}
groupType={focusedGroup?.type}
loading={!sharingReady}
onCopyShareLink={copyShareLink}
shareURI={shareURI}
/>
<ShareAnnotations />
</TabPanel>
<TabPanel
id="export-panel"
Expand All @@ -207,17 +73,7 @@ function ShareDialog({ toastMessenger }: ShareDialogProps) {
</Card>
</>
)}
{!tabbedDialog && (
<SharePanelContent
groupName={focusedGroup?.name}
groupType={focusedGroup?.type}
loading={!sharingReady}
onCopyShareLink={copyShareLink}
shareURI={shareURI}
/>
)}
{!tabbedDialog && <ShareAnnotations />}
</SidebarPanel>
);
}

export default withServices(ShareDialog, ['toastMessenger']);
Loading

0 comments on commit c0eb4f6

Please sign in to comment.