Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calendar updates #116

Merged
merged 2 commits into from
Jul 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ jobs:
# Build the documentation.
- name: Build docs
env:
GOOGLE_CALENDAR_ID: ${{ secrets.GOOGLE_CALENDAR_ID }}
GOOGLE_CALENDAR_API_KEY: ${{ secrets.GOOGLE_CALENDAR_API_KEY }}
run: sphinx-build -b=html docs/ build/

Expand Down
18 changes: 11 additions & 7 deletions docs/demo/kitchen-sink/calendar.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,26 @@ Calendar
========

The napari calendar is an HTML element you can include anywhere using the
``napari-calendar`` directive:
``calendar`` directive:

.. code-block:: rst

.. napari-calendar::
.. calendar::
:calendar-id: c_35r93ec6vtp8smhm7dv5uot0v4@group.calendar.google.com

To use the calendar, you'll need to find out the calendar ID of the calendar you
want to use, and create a Google Calendar API token:
https://console.developers.google.com/flows/enableapi?apiid=calendar

Next, you'll need to pass the calendar ID and API key to the Sphinx build
process as environment variables:
Next, you'll need to pass the API key to the Sphinx build process as environment
variables:

.. code-block:: sh

GOOGLE_CALENDAR_ID=calendar-id GOOGLE_CALENDAR_API_KEY=api-key sphinx-build -b=html docs/ dist/
GOOGLE_CALENDAR_API_KEY=api-key sphinx-build -b=html docs/ dist/

.. napari-calendar::
.. calendar::
:calendar-id: c_35r93ec6vtp8smhm7dv5uot0v4@group.calendar.google.com

-------
Filters
Expand All @@ -32,8 +34,10 @@ meetings, but in the future we could make it configurable.

.. code-block:: rst

.. napari-calendar::
.. calendar::
:calendar-id: c_35r93ec6vtp8smhm7dv5uot0v4@group.calendar.google.com
:show-filters:

.. napari-calendar::
:calendar-id: c_35r93ec6vtp8smhm7dv5uot0v4@group.calendar.google.com
:show-filters:
4 changes: 1 addition & 3 deletions src/napari_sphinx_theme/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,6 @@ def get_edit_url():

# -----------------------------------------------------------------------------

GOOGLE_CALENDAR_ID = os.environ.get('GOOGLE_CALENDAR_ID', '')
GOOGLE_CALENDAR_API_KEY = os.environ.get('GOOGLE_CALENDAR_API_KEY', '')

def add_google_calendar_secrets(app, exception):
Expand All @@ -523,7 +522,6 @@ def add_google_calendar_secrets(app, exception):

with open(script_path, 'r') as f:
source = f.read()
source = source.replace('{google_calendar_id}', GOOGLE_CALENDAR_ID)
source = source.replace('{google_calendar_api_key}', GOOGLE_CALENDAR_API_KEY)

with open(script_path, 'w') as f:
Expand All @@ -548,7 +546,7 @@ def setup(app):
app.connect("html-page-context", update_templates)
app.connect("build-finished", add_google_calendar_secrets)

app.add_directive('napari-calendar', CalendarDirective)
app.add_directive('calendar', CalendarDirective)

# Include templates for sidebar
app.config.templates_path.append(str(theme_path / "_templates"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { CalendarProvider } from './context';
import { CopyCalendarButton } from './CopyCalendarButton';

interface Props {
calendarID: string;
filter?: boolean;
}

Expand All @@ -17,15 +18,13 @@ interface Props {
* For screens smaller than 900px, a week view is rendered with events for the
* current week. Otherwise, a month view is used.
*/
export function Calendar({ filter }: Props) {
export function Calendar({ calendarID, filter }: Props) {
return (
<div className="tw-flex tw-flex-col tw-flex-1 tw-items-stretch">
<CalendarProvider>
<CalendarProvider calendarID={calendarID}>
<div>
<CopyCalendarButton
href={`https://calendar.google.com/calendar/u/0/r?cid=${
process.env.GOOGLE_CALENDAR_ID ?? ''
}`}
href={`https://calendar.google.com/calendar/u/0/r?cid=${calendarID}`}
>
Copy to calendar
</CopyCalendarButton>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export function CalendarTile({ date }: Props) {
<ul className="tw-flex tw-flex-col tw-space-y-1 tw-m-0 tw-p-0">
{eventList.map((event) => (
<CalendarEventButton
key={event.title + event.start.toString()}
key={event.id}
date={dayjs(event.start)}
event={event}
width={tileWidth}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ interface CalendarContextValue {
const CalenderContext = createContext<CalendarContextValue | null>(null);

interface Props {
calendarID: string;
children: ReactNode;
}

/**
* Provider that shares global state within the Calendar component tree.
*/
export function CalendarProvider({ children }: Props) {
export function CalendarProvider({ calendarID, children }: Props) {
const calendarState = useConstant(() =>
proxy<CalendarState>({
activeStartDate: dayjs(),
Expand Down Expand Up @@ -68,7 +69,7 @@ export function CalendarProvider({ children }: Props) {
);

// Fetch calendar events when the active month changes.
useFetchCalendarEvents(calendarState);
useFetchCalendarEvents(calendarID, calendarState);

const calendarContext = useMemo(
() => ({
Expand Down
1 change: 1 addition & 0 deletions src/napari_sphinx_theme/assets/components/Calendar/gapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export async function fetchEvents(
start,
title,
type,
id: event.id,
description: event.description,
htmlLink: event.htmlLink,
location: event.location ?? '',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { Dayjs } from 'dayjs';
export type MeetingType = 'community' | 'workingGroup' | 'other';

export interface CalendarEvent {
id: string;
calendarId: string;
description: string;
end: Dayjs;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import { CalendarState } from './types';
/**
* Effect for fetching the calendar event state when the user changes the
* current month.
*
* @param calendarState The shard calendar state.
*/
export function useFetchCalendarEvents(calendarState: CalendarState): void {
export function useFetchCalendarEvents(
calendarID: string,
calendarState: CalendarState,
): void {
const { activeStartDate } = useSnapshot(calendarState);
const prevActiveStartDate = usePrevious(activeStartDate);

Expand All @@ -34,10 +35,7 @@ export function useFetchCalendarEvents(calendarState: CalendarState): void {

await maybeLoadCalendarAPI(process.env.GOOGLE_CALENDAR_API_KEY ?? '');

calendarState.events = await fetchEvents(
process.env.GOOGLE_CALENDAR_ID ?? '',
activeStartDate,
);
calendarState.events = await fetchEvents(calendarID, activeStartDate);
}

// Wrapper without `async` so that we can call it like a normal function
Expand All @@ -49,5 +47,5 @@ export function useFetchCalendarEvents(calendarState: CalendarState): void {

// Fetch events on initial load.
fetchCalendarEvents();
}, [activeStartDate, calendarState, prevActiveStartDate]);
}, [activeStartDate, calendarID, calendarState, prevActiveStartDate]);
}
29 changes: 21 additions & 8 deletions src/napari_sphinx_theme/assets/napari.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -340,14 +340,27 @@ function renderCalendars() {
document.querySelectorAll('.napari-calendar'),
);

calendarNodes.forEach((node) =>
render(
<MaterialUIProvider>
<Calendar filter={node.classList.contains('show-filters')} />
</MaterialUIProvider>,
node,
),
);
calendarNodes.forEach((node) => {
const calendarID = Array.from(node.classList)
.map((className) =>
className.startsWith('calendar-id')
? className.split('calendar-id-')[1]
: undefined,
)
.find(Boolean);

if (calendarID) {
render(
<MaterialUIProvider>
<Calendar
calendarID={calendarID}
filter={node.classList.contains('show-filters')}
/>
</MaterialUIProvider>,
node,
);
}
});
}

function fixSearchInput() {
Expand Down
8 changes: 7 additions & 1 deletion src/napari_sphinx_theme/calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,16 @@ class CalendarDirective(SphinxDirective):
has_content = True
option_spec: OptionSpec = {
'show-filters': directives.flag,
'calendar-id': directives.unchanged_required,
}

def run(self) -> List[nodes.Node]:
classes = ['napari-calendar']
classes = [
'napari-calendar',
# Hacky approach to pass calendar ID to div since it's not possible to
# pass HTML attributes using docutils :(
f'calendar-id-{self.options["calendar-id"]}',
]

if 'show-filters' in self.options:
classes.append('show-filters')
Expand Down
1 change: 0 additions & 1 deletion webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ const config = {
new webpack.EnvironmentPlugin({
ENV: process.env.ENV || 'local',
GOOGLE_CALENDAR_API_KEY: '{google_calendar_api_key}',
GOOGLE_CALENDAR_ID: '{google_calendar_id}',
NODE_ENV: env,
}),
],
Expand Down