Skip to content

Commit

Permalink
Merge pull request #4895 from h3poteto/iss-4887/dark-mode
Browse files Browse the repository at this point in the history
refs #4887 Dark mode
  • Loading branch information
h3poteto authored Mar 22, 2024
2 parents bffa45e + 82393ae commit 1279e04
Show file tree
Hide file tree
Showing 20 changed files with 434 additions and 53 deletions.
3 changes: 3 additions & 0 deletions locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,9 @@
"title": "Settings",
"language": "Language",
"font_size": "Font size",
"mode": "Color mode",
"dark_mode": "Dark mode",
"light_mode": "Light mode",
"theme": "Color theme"
},
"thirdparty": {
Expand Down
19 changes: 19 additions & 0 deletions renderer/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,19 @@
background-color: rgb(241 245 249);
}

.dark {
.timeline-scrollable::-webkit-scrollbar-thumb {
background-color: #616161;
border-radius: 4px !important;
opacity: 0;
}

.timeline-scrollable::-webkit-scrollbar-track {
border-radius: 4px !important;
background-color: #424242;
}
}

.timeline-scrollable:hover::-webkit-scrollbar-thumb {
opacity: 1;
}
Expand Down Expand Up @@ -329,3 +342,9 @@ a {
@apply text-blue-gray-600;
}
}

.dark {
a {
@apply text-blue-400;
}
}
1 change: 1 addition & 0 deletions renderer/components/Jump.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export default function Jump(props: Props) {
value={keyword}
onChange={e => setKeyword(e.target.value)}
size="lg"
color="blue-gray"
label={formatMessage({ id: 'search.placeholder' })}
ref={inputRef}
/>
Expand Down
8 changes: 4 additions & 4 deletions renderer/components/Media.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ export default function Media(props: Props) {
<Dialog open={props.open} handler={props.close} size="lg">
<DialogBody className="max-h-full max-w-full">
<div className="flex">
<Button variant="text" onClick={previous} disabled={index < 1}>
<FaChevronLeft />
<Button variant="text" color="teal" onClick={previous} disabled={index < 1}>
<FaChevronLeft className="text-lg" />
</Button>
{props.media[index] && (
<img
Expand All @@ -68,8 +68,8 @@ export default function Media(props: Props) {
style={{ maxWidth: '85%' }}
/>
)}
<Button variant="text" onClick={next} disabled={index >= props.media.length - 1}>
<FaChevronRight />
<Button variant="text" color="teal" onClick={next} disabled={index >= props.media.length - 1}>
<FaChevronRight className="text-lg" />
</Button>
</div>
</DialogBody>
Expand Down
50 changes: 46 additions & 4 deletions renderer/components/Settings.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { localeType } from '@/provider/i18n'
import { Dialog, DialogBody, DialogHeader, Input, Option, Select, Typography } from '@material-tailwind/react'
import { Dialog, DialogBody, DialogHeader, Input, Option, Radio, Select, Typography } from '@material-tailwind/react'
import { ChangeEvent, useEffect, useState } from 'react'
import { FormattedMessage } from 'react-intl'

Expand Down Expand Up @@ -55,6 +55,7 @@ export default function Settings(props: Props) {
const [language, setLanguage] = useState<localeType>('en')
const [fontSize, setFontSize] = useState<number>(16)
const [theme, setTheme] = useState<string>('theme-blue')
const [isDark, setIsDark] = useState(false)

useEffect(() => {
if (typeof localStorage !== 'undefined') {
Expand All @@ -64,6 +65,12 @@ export default function Settings(props: Props) {
} else {
setLanguage('en')
}
const dark = localStorage.getItem('color-mode')
if (dark === 'dark') {
setIsDark(true)
} else {
setIsDark(false)
}
}
}, [])

Expand Down Expand Up @@ -91,6 +98,18 @@ export default function Settings(props: Props) {
props.reloadSettings()
}

const modeChanged = (isDark: boolean) => {
setIsDark(isDark)
if (typeof localStorage !== 'undefined') {
if (isDark) {
localStorage.setItem('color-mode', 'dark')
} else {
localStorage.setItem('color-mode', 'light')
}
}
props.reloadSettings()
}

return (
<Dialog open={props.opened} handler={props.close} size="sm">
<DialogHeader>
Expand All @@ -105,7 +124,7 @@ export default function Settings(props: Props) {
</Typography>
</div>
<div>
<Input type="number" value={fontSize} onChange={fontSizeChanged} />
<Input type="number" color="blue-gray" value={fontSize} onChange={fontSizeChanged} />
</div>
</div>
<div>
Expand All @@ -115,7 +134,7 @@ export default function Settings(props: Props) {
</Typography>
</div>
<div>
<Select id="language" onChange={languageChanged} value={language}>
<Select id="language" color="blue-gray" onChange={languageChanged} value={language}>
{languages.map(lang => (
<Option key={lang.value} value={lang.value}>
{lang.label}
Expand All @@ -124,14 +143,37 @@ export default function Settings(props: Props) {
</Select>
</div>
</div>
<div>
<div className="mb-2">
<Typography>
<FormattedMessage id="settings.mode" />
</Typography>
</div>
<div>
<Radio
name="mode"
color="blue"
label={<FormattedMessage id="settings.dark_mode" />}
onClick={() => modeChanged(true)}
defaultChecked={isDark}
/>
<Radio
name="mode"
color="blue"
label={<FormattedMessage id="settings.light_mode" />}
onClick={() => modeChanged(false)}
defaultChecked={!isDark}
/>
</div>
</div>
<div>
<div className="mb-2">
<Typography>
<FormattedMessage id="settings.theme" />
</Typography>
</div>
<div>
<Select id="theme" onChange={themeChanged} value={theme}>
<Select id="theme" color="blue-gray" onChange={themeChanged} value={theme}>
{themes.map(t => (
<Option key={t.value} value={t.value}>
{t.label}
Expand Down
4 changes: 2 additions & 2 deletions renderer/components/accounts/New.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ export default function New(props: NewProps) {
<FormattedMessage id="accounts.new.domain" />
</Typography>
</div>
<Input type="text" id="domain" placeholder="mastodon.social" />
<Input type="text" color="blue-gray" id="domain" placeholder="mastodon.social" />
<Button onClick={checkDomain} loading={loading} color="blue">
<FormattedMessage id="accounts.new.sign_in" />
</Button>
Expand All @@ -185,7 +185,7 @@ export default function New(props: NewProps) {
<FormattedMessage id="accounts.new.authorization_helper" />
</Typography>
</div>
<Input id="authorization" type="text" />
<Input id="authorization" color="blue-gray" type="text" />
</>
)}

Expand Down
8 changes: 4 additions & 4 deletions renderer/components/compose/Compose.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ export default function Compose(props: Props) {
<Input
id="spoiler"
type="text"
color="blue"
color="blue-gray"
containerProps={{ className: 'mb-2' }}
value={spoiler}
onChange={ev => setSpoiler(ev.target.value)}
Expand All @@ -246,7 +246,7 @@ export default function Compose(props: Props) {
<div className="relative">
<Textarea
id="body"
color="blue"
color="blue-gray"
className="resize-none focus:ring-0"
placeholder={formatMessage({ id: 'compose.placeholder' })}
rows={3}
Expand Down Expand Up @@ -481,7 +481,7 @@ const PollForm = (props: PollProps) => {
)}
<Input
type="text"
color="blue"
color="blue-gray"
value={option}
onChange={ev => updateOption(index, ev.target.value)}
containerProps={{ className: 'h-8' }}
Expand All @@ -495,7 +495,7 @@ const PollForm = (props: PollProps) => {
</Button>
<Select
id="expires"
color="blue"
color="blue-gray"
value={`${props.poll.expires_in}`}
onChange={e => changeExpire(parseInt(e))}
containerProps={{ className: 'h-8' }}
Expand Down
2 changes: 1 addition & 1 deletion renderer/components/detail/Detail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export default function Detail(props: Props) {
return (
<>
{target && (
<div className={`bg-white ${props.className}`} style={props.style}>
<div className={`${props.className}`} style={props.style}>
<div className="theme-bg-secondary text-gray-200 flex justify-between p-2 items-center" style={{ height: '44px' }}>
<div className="flex gap-4 items-center">
<button className="text-lg" title={formatMessage({ id: 'detail.back' })}>
Expand Down
22 changes: 14 additions & 8 deletions renderer/components/detail/Profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ export default function Profile(props: Props) {
)}
<Popover open={popoverDetail} handler={setPopoverDetail}>
<PopoverHandler>
<IconButton variant="outlined" title={formatMessage({ id: 'profile.detail' })}>
<IconButton variant="outlined" color="teal" title={formatMessage({ id: 'profile.detail' })}>
<FaEllipsisVertical />
</IconButton>
</PopoverHandler>
Expand Down Expand Up @@ -230,19 +230,25 @@ export default function Profile(props: Props) {
</div>
</div>
<div className="pt-4">
<div className="font-bold" dangerouslySetInnerHTML={{ __html: emojify(user.display_name, user.emojis) }} />
<div className="text-gray-500">@{user.acct}</div>
<div
className="font-bold text-gray-950 dark:text-gray-300"
dangerouslySetInnerHTML={{ __html: emojify(user.display_name, user.emojis) }}
/>
<div className="text-gray-600 dark:text-gray-500">@{user.acct}</div>
<div className="mt-4 raw-html profile" onClick={profileClicked}>
<span
dangerouslySetInnerHTML={{ __html: emojify(user.note, user.emojis) }}
className="overflow-hidden break-all text-gray-800"
className="overflow-hidden break-all text-gray-800 dark:text-gray-400"
/>
</div>
<div className="bg-gray-100 overflow-hidden break-all raw-html mt-2 profile" onClick={profileClicked}>
<div className="bg-gray-100 dark:bg-gray-800 overflow-hidden break-all raw-html mt-2 profile" onClick={profileClicked}>
{user.fields.map((data, index) => (
<dl key={index} className="px-4 py-2 border-gray-200 border-b">
<dt className="text-gray-500">{data.name}</dt>
<dd className="text-gray-700" dangerouslySetInnerHTML={{ __html: emojify(data.value, user.emojis) }} />
<dl key={index} className="px-4 py-2 border-gray-200 dark:border-gray-600 border-b">
<dt className="text-gray-500 dark:text-gray-500">{data.name}</dt>
<dd
className="text-gray-700 dark:text-gray-400"
dangerouslySetInnerHTML={{ __html: emojify(data.value, user.emojis) }}
/>
</dl>
))}
</div>
Expand Down
7 changes: 5 additions & 2 deletions renderer/components/detail/profile/User.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@ export default function User(props: Props) {
}

return (
<div className="border-b mr-2 py-1">
<div className="border-b border-gray-200 dark:border-gray-700 mr-2 py-1">
<div className="flex" onClick={() => openUser(props.user.id)}>
<div className="p2 cursor-pointer" style={{ width: '56px' }}>
<Avatar src={props.user.avatar} />
</div>
<div>
<p className="text-gray-800" dangerouslySetInnerHTML={{ __html: emojify(props.user.display_name, props.user.emojis) }} />
<p
className="text-gray-800 dark:text-gray-200"
dangerouslySetInnerHTML={{ __html: emojify(props.user.display_name, props.user.emojis) }}
/>
<p className="text-gray-500">@{props.user.acct}</p>
</div>
</div>
Expand Down
15 changes: 14 additions & 1 deletion renderer/components/layouts/account.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export default function Layout({ children }: LayoutProps) {
const [style, setStyle] = useState<CSSProperties>({})
const [openPopover, setOpenPopover] = useState(false)
const [theme, setTheme] = useState('theme-blue')
const [isDark, setIsDark] = useState(false)

const { switchLang } = useContext(Context)
const router = useRouter()
Expand Down Expand Up @@ -69,6 +70,11 @@ export default function Layout({ children }: LayoutProps) {
}
}, [])

useEffect(() => {
console.log('isDark', isDark)
document.body.className = isDark ? 'dark' : 'light'
}, [isDark])

const closeNewModal = async () => {
const acct = await db.accounts.toArray()
setAccounts(acct)
Expand Down Expand Up @@ -121,12 +127,19 @@ export default function Layout({ children }: LayoutProps) {
if (t && t.length > 0) {
setTheme(t)
}
const dark = localStorage.getItem('color-mode')
console.log(dark)
if (dark && dark === 'dark') {
setIsDark(true)
} else {
setIsDark(false)
}
}
}

return (
<div className={`app flex flex-col min-h-screen ${theme}`} style={style}>
<main className="flex w-full box-border my-0 mx-auto min-h-screen">
<main className="flex w-full box-border my-0 mx-auto min-h-screen bg-white dark:bg-gray-900">
<aside className="w-16 theme-account-bg flex flex-col justify-between">
<div>
{accounts.map(account => (
Expand Down
2 changes: 1 addition & 1 deletion renderer/components/report/Report.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export default function Report(props: Props) {
<Typography>
<FormattedMessage id="report.detail" />
</Typography>
<Textarea id="comment" rows={4} />
<Textarea id="comment" color="blue-gray" rows={4} />
</div>
<Button className="mt-2" onClick={submit}>
<FormattedMessage id="report.submit" />
Expand Down
16 changes: 11 additions & 5 deletions renderer/components/timelines/notification/Follow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@ export default function Follow(props: Props) {
}

return (
<div className="border-b mr-2 py-1">
<div className="border-b border-gray-200 dark:border-gray-800 mr-2 py-1">
<div className="flex items-center">
<div style={{ width: '56px' }}>
<FaUserPlus className="text-blue-600 w-4 mr-2 ml-auto" />
</div>
<div className="cursor-pointer" style={{ width: 'calc(100% - 56px)' }} onClick={() => openUser(props.notification.account.id)}>
<div
className="cursor-pointer text-gray-950 dark:text-gray-300"
style={{ width: 'calc(100% - 56px)' }}
onClick={() => openUser(props.notification.account.id)}
>
<span
dangerouslySetInnerHTML={{
__html: emojify(
Expand Down Expand Up @@ -52,12 +56,14 @@ export default function Follow(props: Props) {
<div style={{ width: 'calc(100% - 56px)' }}>
<div className="flex cursor-pointer" onClick={() => openUser(props.notification.account.id)}>
<span
className="text-gray-950 text-ellipsis break-all overflow-hidden"
className="text-gray-950 dark:text-gray-300 text-ellipsis break-all overflow-hidden"
dangerouslySetInnerHTML={{ __html: emojify(props.notification.account.display_name, props.notification.account.emojis) }}
></span>
<span className="text-gray-600 text-ellipsis break-all overflow-hidden">@{props.notification.account.acct}</span>
<span className="text-gray-600 dark:text-gray-500 text-ellipsis break-all overflow-hidden">
@{props.notification.account.acct}
</span>
</div>
<div className="text-gray-600">
<div className="text-gray-600 dark:text-gray-500">
<FormattedMessage id="notification.follow.followers" values={{ num: props.notification.account.followers_count }} />
</div>
</div>
Expand Down
Loading

0 comments on commit 1279e04

Please sign in to comment.