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

RTL Support #4168

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
6 changes: 3 additions & 3 deletions _locales/_languages.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"ar": "العربیة",
"ar": { "name": "العربیة", "dir": "rtl" },
"az": "Azeri",
"bg": "Български език",
"bqi": "Luri Bakhtiari",
"ca": "Català",
"ckb": "کوردی",
"ckb": { "name": "کوردی", "dir": "rtl" },
"cs": "Čeština",
"da": "Dansk",
"de": "Deutsch",
Expand All @@ -13,7 +13,7 @@
"eo": "Esperanto",
"es": "Español",
"eu": "Euskara",
"fa": "فارسی",
"fa": { "name": "فارسی", "dir": "rtl" },
"fi": "Suomi",
"fr": "Français",
"gl": "Galego",
Expand Down
1 change: 1 addition & 0 deletions docs/STYLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ We use `prettier` for CSS formatting. Use `pnpm -w fix:format` before committing
### General

- Prefer `padding-top`, `padding-bottom`, `padding-left` etc. over `padding` to avoid bugs through order-critical dependencies between properties, except when you only need `padding` or `border` etc.
- Avoid `padding-right`, `margin-left`, `float: left` and so on, instead use `padding-inline-end`, `margin-inline-start` and `float: inline-start` to avoid bugs with RTL Layout (some languages are written right to left, so the interface should also be "mirrored").
- Try to avoid hacky `!important` as much as possible (goal is to get rid of them altogether at one point), if your components are well composed and styles isolated you don't need them
- Avoid setting styles directly on elements via the `style` attribute. Exception: the value is dynamically set through an JavaScript variable
- Prefer `0` instead of `0px` when setting zero values
Expand Down
14 changes: 7 additions & 7 deletions packages/frontend/scss/chat/_chat-list-item.scss
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
margin-top: 1px;

font-size: 9pt;
margin-left: 5px;
margin-inline-start: 5px;
height: 20px;
padding: 0 5pt;
line-height: 20px;
Expand All @@ -33,7 +33,7 @@

& > .content {
flex-grow: 1;
margin-left: 10px;
margin-inline-start: 10px;
// parent - 48px (for avatar) - 10px (our right padding)
max-width: calc(100% - 58px);

Expand Down Expand Up @@ -76,7 +76,7 @@

& > div > .timestamp {
flex-shrink: 0;
margin-left: 6px;
margin-inline-start: 6px;
font-size: 11px;
line-height: 16px;
letter-spacing: 0.3px;
Expand Down Expand Up @@ -128,8 +128,8 @@
}

& > .summary {
float: left;
margin-right: 2px;
float: inline-start;
margin-inline-end: 2px;
&.draft {
color: var(--draftTextColor);
}
Expand All @@ -142,7 +142,7 @@
border-radius: 2px;
font-size: 11px;
padding: 1px 4px;
margin-left: 2px;
margin-inline-start: 2px;
}
}

Expand Down Expand Up @@ -253,7 +253,7 @@
@include color-svg('./images/pin.svg', var(--globalText));
width: 16px;
height: 16px;
margin-left: 6px;
margin-inline-start: 6px;
}
}

Expand Down
6 changes: 3 additions & 3 deletions packages/frontend/scss/contact/_contact-list.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.contact-list-item {
display: flex;
padding-left: 20px;
padding-inline-start: 20px;

&:hover {
background-color: var(--contactListItemBgHover);
Expand All @@ -11,7 +11,7 @@
}

.chat-list & {
padding-left: 0px;
padding-inline-start: 0px;
}

&[disabled] {
Expand All @@ -23,7 +23,7 @@
}

.delta-checkbox {
margin-right: 10px;
margin-inline-end: 10px;
input {
-webkit-appearance: none;
width: 20px;
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/scss/contact/_contact.scss
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
display: inline-block;
width: calc(100% - 64px);
// height: 54px;
margin-left: 10px;
margin-inline-start: 10px;
.chat-list & {
width: calc(100% - 37px);
}
Expand Down
4 changes: 2 additions & 2 deletions packages/frontend/scss/main_screen/_navbar_wrapper.scss
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@
}

span.views {
margin-left: auto;
margin-right: 15px;
margin-inline-start: auto;
margin-inline-end: 15px;
display: flex;
}

Expand Down
8 changes: 8 additions & 0 deletions packages/frontend/scss/overwrites/_bp4-overwrites.scss
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,11 @@ input[type='text']:focus {
.bp4-tab-list {
padding: 5px;
}

.bp4-navbar-group.bp4-align-left {
float: inline-start;
}

.bp4-navbar-group.bp4-align-right {
float: inline-end;
}
35 changes: 26 additions & 9 deletions packages/frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ import { runPostponedFunctions } from './onready'
import { I18nContext } from './contexts/I18nContext'

export default function App(_props: any) {
const [localeData, setLocaleData] = useState<LocaleData | null>(null)

useEffect(() => {
runtime.emitUIReady()
window.addEventListener('keydown', function (ev: KeyboardEvent) {
Expand Down Expand Up @@ -48,6 +46,23 @@ export default function App(_props: any) {
useLayoutEffect(() => {
startBackendLogging()
runPostponedFunctions()
}, [])

return (
<CrashScreen>
<ThemeContextWrapper>
<I18nContextWrapper>
<ScreenController />
</I18nContextWrapper>
</ThemeContextWrapper>
</CrashScreen>
)
}

function I18nContextWrapper({ children }: { children: React.ReactChild }) {
const [localeData, setLocaleData] = useState<LocaleData | null>(null)

useLayoutEffect(() => {
;(async () => {
const desktop_settings = await runtime.getDesktopSettings()
await reloadLocaleData(desktop_settings.locale || 'en')
Expand All @@ -72,15 +87,17 @@ export default function App(_props: any) {

if (!localeData) return null
return (
<CrashScreen>
<ThemeContextWrapper>
<I18nContext.Provider value={window.static_translate}>
<ScreenController />
</I18nContext.Provider>
</ThemeContextWrapper>
</CrashScreen>
<I18nContext.Provider
value={{
tx: window.static_translate,
writingDirection: window.localeData.dir,
}}
>
<div dir={window.localeData.dir}>{children}</div>
</I18nContext.Provider>
)
}

function ThemeContextWrapper({ children }: { children: React.ReactChild }) {
/** on each theme change this var changes */
const [theme_rand, setThemeRand] = useState(0)
Expand Down
3 changes: 3 additions & 0 deletions packages/frontend/src/components/Gallery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import FullscreenMedia, {
NeighboringMediaMode,
} from './dialogs/FullscreenMedia'
import { DialogContext } from '../contexts/DialogContext'
import { useTranslationWritingDirection } from '../hooks/useTranslationFunction'

const log = getLogger('renderer/Gallery')

Expand Down Expand Up @@ -411,6 +412,7 @@ function FileTable({
mediaLoadResult: Record<number, Type.MessageLoadResult>
queryText: string
}) {
const writingDirection = useTranslationWritingDirection()
return (
<FixedSizeList
width={width}
Expand All @@ -419,6 +421,7 @@ function FileTable({
itemCount={mediaMessageIds.length}
overscanCount={10}
itemData={mediaMessageIds}
direction={writingDirection}
>
{({ index, style, data }) => {
const msgId = data[index]
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/components/LoginForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ export default function LoginForm({ credentials, setCredentials }: LoginProps) {

return (
<I18nContext.Consumer>
{tx => (
{({ tx }) => (
<div className='login-form'>
<DeltaInput
key='addr'
Expand Down
7 changes: 6 additions & 1 deletion packages/frontend/src/components/chat/ChatList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ import useChat from '../../hooks/chat/useChat'
import useCreateChatByContactId from '../../hooks/chat/useCreateChatByContactId'
import useDialog from '../../hooks/dialog/useDialog'
import useKeyBindingAction from '../../hooks/useKeyBindingAction'
import useTranslationFunction from '../../hooks/useTranslationFunction'
import useTranslationFunction, {
useTranslationWritingDirection,
} from '../../hooks/useTranslationFunction'

import type {
ChatListItemData,
Expand Down Expand Up @@ -76,6 +78,8 @@ export function ChatListPart({
| MessageChatListItemData
itemHeight: number
}) {
const writingDirection = useTranslationWritingDirection()

const infiniteLoaderRef = useRef<InfiniteLoader | null>(null)

// By default InfiniteLoader assumes that each item's index in the list
Expand Down Expand Up @@ -115,6 +119,7 @@ export function ChatListPart({
width={width}
itemKey={itemKey}
itemData={itemData}
direction={writingDirection}
>
{children}
</List>
Expand Down
41 changes: 25 additions & 16 deletions packages/frontend/src/components/composer/ComposerMessageInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import debounce from 'debounce'
import { ActionEmitter, KeybindAction } from '../../keybindings'
import { getLogger } from '../../../../shared/logger'
import { DialogContext } from '../../contexts/DialogContext'
import { I18nContext } from '../../contexts/I18nContext'

const log = getLogger('renderer/composer/ComposerMessageInput')

Expand Down Expand Up @@ -214,22 +215,30 @@ export default class ComposerMessageInput extends React.Component<

render() {
return (
<textarea
className='message-input-area'
id='composer-textarea'
ref={this.textareaRef}
rows={1}
// intent={this.state.error ? 'danger' : 'none'}
// large
value={this.state.text}
onKeyDown={this.onKeyDown}
onChange={this.onChange}
onPaste={this.props.onPaste}
placeholder={window.static_translate('write_message_desktop')}
disabled={this.state.loadingDraft}
dir='auto'
spellCheck={true}
/>
<I18nContext.Consumer>
{({ writingDirection }) => (
<textarea
className='message-input-area'
id='composer-textarea'
ref={this.textareaRef}
rows={1}
// intent={this.state.error ? 'danger' : 'none'}
// large
value={this.state.text}
onKeyDown={this.onKeyDown}
onChange={this.onChange}
onPaste={this.props.onPaste}
placeholder={window.static_translate('write_message_desktop')}
disabled={this.state.loadingDraft}
dir={
writingDirection === 'rtl'
? 'rtl'
: 'auto' /* auto is based on content but defaults to ltr */
}
spellCheck={true}
/>
)}
</I18nContext.Consumer>
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, {
ChangeEvent,
useCallback,
useContext,
useEffect,
useLayoutEffect,
useRef,
Expand All @@ -16,11 +17,11 @@ import { BackendRemote, onDCEvent, Type } from '../../../backend-com'
import { selectedAccountId } from '../../../ScreenController'
import { DialogBody, DialogHeader, OkCancelFooterAction } from '../../Dialog'
import useDialog from '../../../hooks/dialog/useDialog'
import useTranslationFunction from '../../../hooks/useTranslationFunction'
import { VerifiedContactsRequiredDialog } from '../ProtectionStatusDialog'
import InfiniteLoader from 'react-window-infinite-loader'
import { AddMemberChip } from './AddMemberDialog'
import styles from './styles.module.scss'
import { I18nContext } from '../../../contexts/I18nContext'

export function AddMemberInnerDialog({
onCancel,
Expand Down Expand Up @@ -53,7 +54,7 @@ export function AddMemberInnerDialog({
isBroadcast: boolean
isVerificationRequired: boolean
}) {
const tx = useTranslationFunction()
const { tx, writingDirection } = useContext(I18nContext)
const { openDialog } = useDialog()
const accountId = selectedAccountId()
const contactIdsInGroup: number[] = contactIds.filter(contactId =>
Expand Down Expand Up @@ -290,6 +291,7 @@ export function AddMemberInnerDialog({
// which might be different, e.g. currently they're smaller for
// "Rocket Theme", which results in gaps between the elements.
itemSize={64}
direction={writingDirection}
>
{({ index, style, data: contactIds }) => {
const isExtraItem = index >= contactIds.length
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import styles from './styles.module.scss'
import { makeContextMenu } from '../../ContextMenu'
import { ContextMenuContext } from '../../../contexts/ContextMenuContext'
import ImageCropper from '../../ImageCropper'
import { I18nContext } from '../../../contexts/I18nContext'

type ViewMode = 'main' | 'createGroup' | 'createBroadcastList'

Expand Down Expand Up @@ -119,7 +120,7 @@ type CreateChatMainProps = {

function CreateChatMain(props: CreateChatMainProps) {
const { setViewMode, onClose } = props
const tx = useTranslationFunction()
const { tx, writingDirection } = useContext(I18nContext)
const { userFeedback } = useContext(ScreenContext)
const openConfirmationDialog = useConfirmationDialog()
const accountId = selectedAccountId()
Expand Down Expand Up @@ -293,6 +294,7 @@ function CreateChatMain(props: CreateChatMainProps) {
// which might be different, e.g. currently they're smaller for
// "Rocket Theme", which results in gaps between the elements.
itemSize={64}
direction={writingDirection}
>
{({ index, style }) => {
const item = contactsAndExtraItems[index]
Expand Down
8 changes: 7 additions & 1 deletion packages/frontend/src/contexts/I18nContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,10 @@ import { createContext } from 'react'

import { getMessageFunction } from '../../../shared/localize'

export const I18nContext = createContext<getMessageFunction>(key => key as any)
export const I18nContext = createContext<{
tx: getMessageFunction
writingDirection: 'ltr' | 'rtl'
}>({
tx: key => key as any,
writingDirection: 'ltr',
})
Loading
Loading