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

Frame, member and project detail layout #67

Merged
merged 2 commits into from
Dec 3, 2024
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
31 changes: 31 additions & 0 deletions components/LarkImage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { TableCellValue } from 'mobx-lark';
import { ImageProps } from 'next/image';
import { FC } from 'react';

import { blobURLOf } from '../models/Base';
import { DefaultImage, fileURLOf } from '../pages/api/Lark/file/[id]';

export interface LarkImageProps extends Omit<ImageProps, 'src'> {
src?: TableCellValue;
}

export const LarkImage: FC<LarkImageProps> = ({ src = DefaultImage, alt, ...props }) => (
<img
loading="lazy"
{...props}
src={blobURLOf(src)}
alt={alt}
onError={({ currentTarget: image }) => {
const path = fileURLOf(src),
errorURL = decodeURI(image.src);

if (!path) return;

if (errorURL.endsWith(path)) {
if (!alt) image.src = DefaultImage;
} else if (!errorURL.endsWith(DefaultImage)) {
image.src = path;
}
}}
/>
);
8 changes: 7 additions & 1 deletion components/Layout/ColorModeDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ export default function ColorModeIconDropdown() {
const toggleMode = () => setMode(resolvedMode === 'light' ? 'dark' : 'light');

return (
<IconButton data-screenshot="toggle-mode" size="small" disableRipple onClick={toggleMode}>
<IconButton
color="inherit"
data-screenshot="toggle-mode"
size="small"
disableRipple
onClick={toggleMode}
>
{icon}
</IconButton>
);
Expand Down
26 changes: 25 additions & 1 deletion components/Layout/Footer.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
export const Footer = () => (
<div className="container mx-auto flex max-w-screen-xl py-12 text-center">idea2app</div>
<div className="container mx-auto flex max-w-screen-xl items-center justify-between border-t-2 px-4 py-12 text-center">
© 2024 idea2app
<ul className="flex gap-4">
<li>
<a
className="border-b-2 border-b-black py-1 dark:border-b-white"
href="https://web-cell.dev/"
target="_blank"
rel="noreferrer"
>
Web Cell
Soecka marked this conversation as resolved.
Show resolved Hide resolved
</a>
</li>
<li>
<a
className="border-b-2 border-b-black py-1 dark:border-b-white"
href="https://tech-query.me/"
target="_blank"
rel="noreferrer"
>
TechQuery
</a>
</li>
</ul>
</div>
);
46 changes: 46 additions & 0 deletions components/Layout/Frame.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { DataObject, Filter, ListModel } from 'mobx-restful';
import { FC, ReactNode } from 'react';

import { i18n } from '../../models/Translation';
import { PageHead } from '../PageHead';
import { ScrollList } from '../ScrollList';

export interface FrameProps<D extends DataObject, F extends Filter<D> = Filter<D>> {
store: ListModel<D, F>;
filter?: F;
defaultData?: D[];
title: string;
header: string;
className?: string;
scrollList?: boolean;
children?: ReactNode;
Layout: FC<{ defaultData: D[]; className?: string }>;
}

/**
* @todo remove ScrollList and use children instead?
Soecka marked this conversation as resolved.
Show resolved Hide resolved
*/
export const Frame = <D extends DataObject, F extends Filter<D> = Filter<D>>({
className = '',
scrollList = true,
children,
title,
header,
Layout,
...rest
}: FrameProps<D, F>) => (
<div className={`container mx-auto max-w-screen-xl px-4 pb-6 pt-16 ${className}`}>
<PageHead title={title} />
<h1 className="my-8 text-4xl">{header}</h1>

{scrollList ? (
<ScrollList
translator={i18n}
renderList={allItems => <Layout defaultData={allItems} />}
{...rest}
/>
) : (
children
)}
</div>
);
71 changes: 64 additions & 7 deletions components/Layout/MainNavigator.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
import { AppBar, Drawer, IconButton, PopoverProps, Toolbar } from '@mui/material';
import {
AppBar,
Button,
Drawer,
IconButton,
Menu,
MenuItem,
PopoverProps,
Toolbar
} from '@mui/material';
import { observable } from 'mobx';
import { observer } from 'mobx-react';
import Image from 'next/image';
import Link from 'next/link';
import { Component, SyntheticEvent } from 'react';
import { Component } from 'react';

import { i18n } from '../../models/Translation';
import { i18n, LanguageName } from '../../models/Translation';
import { SymbolIcon } from '../Icon';
import ColorModeIconDropdown from './ColorModeDropdown';
import { GithubIcon } from './Svg';

const { t } = i18n;

Expand All @@ -21,10 +31,10 @@ export const mainNavLinks = () => [
export class MainNavigator extends Component {
@observable accessor menuExpand = false;
@observable accessor menuAnchor: PopoverProps['anchorEl'] = null;
@observable accessor eventKey = 0;

handleChange = (event: SyntheticEvent, newValue: number) => {
this.eventKey = newValue;
switchI18n = (key: string) => {
i18n.changeLanguage(key as keyof typeof LanguageName);
this.menuAnchor = null;
};

renderLinks = () =>
Expand All @@ -34,6 +44,49 @@ export class MainNavigator extends Component {
</Link>
));

renderI18nSwitch = () => {
const { currentLanguage } = i18n,
{ menuAnchor } = this;

return (
<>
<Button
color="inherit"
aria-controls="i18n-menu"
size="small"
id="i18n-selector"
startIcon={<SymbolIcon name="translate" />}
onClick={event => (this.menuAnchor = event.currentTarget)}
>
{LanguageName[currentLanguage]}
</Button>
<Menu
anchorEl={menuAnchor}
id="i18n-menu"
slotProps={{
paper: {
variant: 'outlined',
sx: { my: '4px' }
}
}}
open={Boolean(menuAnchor)}
onClose={() => (this.menuAnchor = null)}
>
{Object.entries(LanguageName).map(([key, name]) => (
<MenuItem
key={key}
value={key}
selected={key === currentLanguage}
onClick={() => this.switchI18n(key)}
>
{name}
</MenuItem>
))}
</Menu>
</>
);
};

renderDrawer = () => (
<nav className="sm:hidden">
<IconButton
Expand Down Expand Up @@ -77,8 +130,12 @@ export class MainNavigator extends Component {

<nav className="item-center hidden flex-row gap-4 sm:flex">{this.renderLinks()}</nav>

<div className="flex flex-row items-center gap-4">
<div className="flex flex-row items-center gap-3 sm:gap-6">
<Link href="https://github.com/idea2app" target="_blank" rel="noopener noreferrer">
<GithubIcon />
</Link>
<ColorModeIconDropdown />
{this.renderI18nSwitch()}
</div>
</div>
</Toolbar>
Expand Down
4 changes: 2 additions & 2 deletions components/Section.tsx → components/Layout/Section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import { Button } from '@mui/material';
import Link from 'next/link';
import { FC, PropsWithChildren } from 'react';

import { i18n } from '../models/Translation';
import { i18n } from '../../models/Translation';

export type SectionProps = PropsWithChildren<
Partial<Record<'id' | 'title' | 'link' | 'className', string>>
>;

const { t } = i18n;

export const Section: FC<SectionProps> = ({ id, title, children, link, className }) => (
export const Section: FC<SectionProps> = ({ id, title, children, link, className = '' }) => (
<section className={`mx-auto flex max-w-screen-xl flex-col gap-6 py-8 ${className}`}>
<h2 className="text-center" id={id}>
{title}
Expand Down
2 changes: 1 addition & 1 deletion components/ScrollList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export class ScrollList<
<div>
{renderList(allItems)}

<footer style={{ marginTop: '1.5rem' }}>
<footer style={{ marginTop: '1.5rem', textAlign: 'center' }}>
TechQuery marked this conversation as resolved.
Show resolved Hide resolved
{noMore || !allItems.length ? t('no_more') : t('load_more')}
</footer>
</div>
Expand Down
17 changes: 16 additions & 1 deletion models/Base.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { HTTPClient } from 'koajax';
import { TableCellValue } from 'mobx-lark';

export const isServer = () => typeof window === 'undefined';

Expand All @@ -11,7 +12,21 @@ export const API_Host = isServer()
: 'http://localhost:3000'
: globalThis.location.origin;

export const blobClient = new HTTPClient({
baseURI: 'https://ows.blob.core.chinacloudapi.cn/$web/',
responseType: 'arraybuffer'
});

export const fileBaseURI = blobClient.baseURI + 'file';

export const larkClient = new HTTPClient({
baseURI: `${API_Host}/api/Lark/`,
responseType: 'json',
responseType: 'json'
});

export const blobURLOf = (value: TableCellValue) =>
value instanceof Array
? typeof value[0] === 'object' && ('file_token' in value[0] || 'attachmentToken' in value[0])
? `${fileBaseURI}/${value[0].name}`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

不要再用文件名了,用 token。

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

R2 那个 PR 合了调通了再改,blobClient 这些都要改

: ''
: value + '';
8 changes: 4 additions & 4 deletions pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ const AppShell = observer(({ Component, pageProps }: AppProps<{}>) => (
* @see {@link https://mui.com/material-ui/integrations/interoperability/#tailwind-css}
*/}
<ThemeProvider theme={theme} defaultMode="system" disableTransitionOnChange>
<MainNavigator />
<div className="flex min-h-screen flex-col justify-between">
<MainNavigator />

<div className="pt-16">
<Component {...pageProps} />
</div>

<Footer />
<Footer />
</div>
</ThemeProvider>
</StyledEngineProvider>
</>
Expand Down
6 changes: 2 additions & 4 deletions pages/api/Lark/bitable/v1/[...slug].ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@ import { proxyLark } from '../../core';

export default proxyLark((path, data) => {
if (path.split('?')[0].endsWith('/records')) {
const items =
(data as LarkPageData<TableRecord<DataObject>>).data!.items || [];
const items = (data as LarkPageData<TableRecord<DataObject>>).data!.items || [];

for (const { fields } of items)
for (const key of Object.keys(fields))
if (!/^\w+$/.test(key)) delete fields[key];
for (const key of Object.keys(fields)) if (!/^\w+$/.test(key)) delete fields[key];
}
return data;
});
2 changes: 2 additions & 0 deletions pages/api/Lark/file/[id].ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { TableCellMedia, TableCellValue } from 'mobx-lark';
import { safeAPI } from '../../core';
import { lark } from '../core';

export const DefaultImage = '/idea2app.svg';

export const fileURLOf = (field: TableCellValue) =>
field instanceof Array
? field[0]
Expand Down
18 changes: 3 additions & 15 deletions pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,23 @@ import { FC } from 'react';
import { PartnerOverview } from '../components/Client/Partner';
import { GitListLayout } from '../components/Git';
import { SymbolIcon } from '../components/Icon';
import { Section } from '../components/Layout/Section';
import { MemberCard } from '../components/Member/Card';
import { PageHead } from '../components/PageHead';
import { Section } from '../components/Section';
import { MEMBER_VIEW, MemberModel } from '../models/Member';
import { GitRepositoryModel } from '../models/Repository';
import { i18n } from '../models/Translation';
import { PARTNERS_INFO, service } from './api/home';

export const getServerSideProps = compose(cache(), errorLogger, translator(i18n), async () => {
const [
// projects,
repositories,
// partners
members
] = await Promise.all([
// new ProjectModel().getList({}, 1, 9),
const [repositories, members] = await Promise.all([
new GitRepositoryModel('idea2app').getList({ relation: [] }, 1, 9),
new MemberModel().getViewList(MEMBER_VIEW)
]);

return {
props: {
// projects: JSON.parse(JSON.stringify(projects)) as Project[],
repositories: JSON.parse(JSON.stringify(repositories)) as GitRepository[],

members: members.filter(({ github, position, summary }) => github && position && summary)
}
};
Expand All @@ -46,7 +38,7 @@ const HomePage: FC<InferGetServerSidePropsType<typeof getServerSideProps>> = obs
<>
<PageHead />

<div className="px-2 py-6">
<div className="px-4 py-6 pt-16">
<section className="container mx-auto flex max-w-screen-lg flex-col gap-4">
<div className="flex flex-row items-center justify-around py-12">
<Image src="/idea2app.svg" width={234} height={220} alt="idea2app logo" />
Expand Down Expand Up @@ -105,10 +97,6 @@ const HomePage: FC<InferGetServerSidePropsType<typeof getServerSideProps>> = obs
<div className="absolute right-0 top-0 z-20 block h-24 w-24 bg-gradient-to-l from-background to-transparent" />
</section>

{/* <Section title={t('latest_projects')} link="/project">
<ProjectListLayout defaultData={projects} />
</Section>*/}
Soecka marked this conversation as resolved.
Show resolved Hide resolved

<Section title={t('member')} link="/member">
<div className="relative max-h-[45rem] overflow-hidden">
<Masonry
Expand Down
Loading