Merge pull request #5490 from urbit/lf/groups-grabbag

groups: misc frontend fixes
This commit is contained in:
Liam Fitzgerald 2022-01-10 11:08:14 -06:00 committed by GitHub
commit b4512bf1a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 64 additions and 52 deletions

View File

@ -6,7 +6,6 @@ const makeIndexes = () => new Map([
['commands', []],
['subscriptions', []],
['groups', []],
['apps', []],
['other', []]
]);
@ -61,29 +60,6 @@ const commandIndex = function (currentGroup, groups, associations) {
return commands;
};
const appIndex = function (apps) {
// all apps are indexed from launch data
// indexed into 'apps'
const applications = [];
Object.keys(apps)
.filter((e) => {
return !['weather','clock'].includes(e);
})
.sort((a, b) => {
return a.localeCompare(b);
})
.map((e) => {
const obj = result(
apps[e].type?.basic?.title || apps[e].type.custom?.tile || e,
apps[e]?.type.basic?.linkedUrl || apps[e]?.type.custom?.linkedUrl || '',
apps[e]?.type?.basic?.title || apps[e].type.custom?.tile || e,
null
);
applications.push(obj);
});
return applications;
};
const otherIndex = function(config) {
const other = [];
const idx = {
@ -102,7 +78,7 @@ const otherIndex = function(config) {
return other;
};
export default function index(contacts, associations, apps, currentGroup, groups, hide): Map<string, OmniboxItem[]> {
export default function index(contacts, associations, currentGroup, groups, hide): Map<string, OmniboxItem[]> {
const indexes = makeIndexes();
indexes.set('ships', shipIndex(contacts));
// all metadata from all apps is indexed
@ -164,7 +140,6 @@ export default function index(contacts, associations, apps, currentGroup, groups
indexes.set('commands', commandIndex(currentGroup, groups, associations));
indexes.set('subscriptions', subscriptions);
indexes.set('groups', landscape);
indexes.set('apps', appIndex(apps));
indexes.set('other', otherIndex(hide));
return indexes;

View File

@ -210,6 +210,9 @@ function more(json: any, state: HarkState): HarkState {
function added(json: any, state: HarkState): HarkState {
if('added' in json) {
const { bin } = json.added;
if(bin.place.desk !== window.desk) {
return state;
}
const binId = harkBinToId(bin);
state.unseen[binId] = json.added;
}
@ -239,6 +242,9 @@ function timebox(json: any, state: HarkState): HarkState {
const time = makePatDa(lid.archive);
const old = state.archive.get(time) || {};
notifications.forEach((note: any) => {
if(note.bin.desk !== window.desk) {
return;
}
const binId = harkBinToId(note.bin);
old[binId] = note;
});
@ -246,6 +252,9 @@ function timebox(json: any, state: HarkState): HarkState {
} else {
const seen = 'seen' in lid ? 'seen' : 'unseen';
notifications.forEach((note: any) => {
if(note.bin.desk !== window.desk) {
return;
}
const binId = harkBinToId(note.bin);
state[seen][binId] = note;
});

View File

@ -41,6 +41,7 @@ export interface SettingsState {
hideUnreads: boolean;
hideGroups: boolean;
hideUtilities: boolean;
disableSpellcheck: boolean;
};
keyboard: ShortcutMapping;
remoteContentPolicy: RemoteContentPolicy;
@ -73,7 +74,8 @@ const useSettingsState = createState<SettingsState>(
hideAvatars: false,
hideUnreads: false,
hideGroups: false,
hideUtilities: false
hideUtilities: false,
disableSpellcheck: false
},
remoteContentPolicy: {
imageShown: true,

View File

@ -43,7 +43,11 @@ const Root = withState(styled.div`
font-family: ${p => p.theme.fonts.sans};
height: 100%;
width: 100%;
padding: 0;
padding-left: env(safe-area-inset-left, 0px);
padding-right: env(safe-area-inset-right, 0px);
padding-top: env(safe-area-inset-top, 0px);
padding-bottom: env(safe-area-inset-bottom, 0px);
margin: 0;
${p => p.display.backgroundType === 'url' ? `
background-image: url('${p.display.background}');

View File

@ -8,6 +8,7 @@ import React, { useRef, ClipboardEvent, useEffect, useImperativeHandle } from 'r
import { Controlled as CodeEditor } from 'react-codemirror2';
import styled from 'styled-components';
import { MOBILE_BROWSER_REGEX } from '~/logic/lib/util';
import useSettingsState from '~/logic/state/settings';
import '../css/custom.css';
import { useChatStore } from './ChatPane';
@ -131,6 +132,8 @@ const ChatEditor = React.forwardRef<CodeMirrorShim, ChatEditorProps>(({ inCodeMo
useImperativeHandle(ref, () => editorRef.current);
const editor = editorRef.current;
const disableSpellcheck = useSettingsState(s => s.calm.disableSpellcheck);
const {
message,
setMessage
@ -234,6 +237,7 @@ const ChatEditor = React.forwardRef<CodeMirrorShim, ChatEditorProps>(({ inCodeMo
fontFamily={inCodeMode ? 'Source Code Pro' : 'Inter'}
fontSize={1}
lineHeight="tall"
spellCheck={!disableSpellcheck}
value={message}
rows={1}
style={{ width: '100%', background: 'transparent', color: 'currentColor' }}

View File

@ -161,6 +161,13 @@ class ChatWindow extends Component<
}
}
onTopLoaded = () => {
const { graphSize, unreadCount } = this.props;
if(graphSize >= unreadCount) {
this.props.dismissUnread();
}
};
onBottomLoaded = () => {
if(this.state.unreadIndex.eq(bigInt.zero)) {
this.calculateUnreadIndex();
@ -274,6 +281,7 @@ class ChatWindow extends Component<
origin='bottom'
style={virtScrollerStyle}
onBottomLoaded={this.onBottomLoaded}
onTopLoaded={this.onTopLoaded}
// @ts-ignore paging @liam-fitzgerald on virtualscroller props
onScroll={this.onScroll}
data={graph}

View File

@ -46,13 +46,6 @@ export function Note(props: NoteProps & RouteComponentProps) {
props.history.push(rootUrl);
};
if (typeof note.post === 'string' || !note.post) {
return (
<Box width="100%" pt="2" textAlign="center">
<Text gray>This note has been deleted.</Text>
</Box>
);
}
const comments = getComments(note);
const [, title, , post] = getLatestRevision(note);
@ -148,4 +141,16 @@ export function Note(props: NoteProps & RouteComponentProps) {
);
}
export default Note;
export default function(props: NoteProps & RouteComponentProps) {
const { note } = props;
if (typeof note.post === 'string' || !note.post) {
return (
<Box width="100%" pt="2" textAlign="center">
<Text gray>This note has been deleted.</Text>
</Box>
);
}
return (<Note {...props} />);
}

View File

@ -20,6 +20,7 @@ interface FormSchema {
audioShown: boolean;
oembedShown: boolean;
videoShown: boolean;
disableSpellcheck: boolean;
}
const settingsSel = (s: SettingsState): FormSchema => ({
@ -28,10 +29,11 @@ const settingsSel = (s: SettingsState): FormSchema => ({
hideUnreads: s.calm.hideUnreads,
hideGroups: s.calm.hideGroups,
hideUtilities: s.calm.hideUtilities,
disableSpellcheck: s.calm.disableSpellcheck,
imageShown: !s.remoteContentPolicy.imageShown,
videoShown: !s.remoteContentPolicy.videoShown,
oembedShown: !s.remoteContentPolicy.oembedShown,
audioShown: !s.remoteContentPolicy.audioShown
audioShown: !s.remoteContentPolicy.audioShown,
});
export function CalmPrefs() {
@ -108,6 +110,12 @@ export function CalmPrefs() {
id="oembedShown"
caption="Embedded content may contain scripts that can track you"
/>
<Text fontWeight="medium">Input settings</Text>
<Toggle
label="Disable spellcheck"
id="disableSpellcheck"
caption="Disable browser spellcheck"
/>
</Col>
</Form>
</FormikOnBlur>

View File

@ -13,6 +13,7 @@ import {
} from 'formik';
import React, { useEffect, useMemo } from 'react';
import * as Yup from 'yup';
import useSettingsState from '~/logic/state/settings';
import { ShipImage } from './ShipImage';
interface FormSchema {
@ -35,6 +36,7 @@ interface CommentInputProps {
const SubmitTextArea = (props) => {
const { submitForm } = useFormikContext<FormSchema>();
const [field] = useField(props.id);
const disableSpellcheck = useSettingsState(s => s.calm.disableSpellcheck);
const onKeyDown = (e: KeyboardEvent) => {
if ((e.getModifierState('Control') || e.metaKey) && e.key === 'Enter') {
submitForm();
@ -50,6 +52,7 @@ const SubmitTextArea = (props) => {
fontWeight="500"
fontSize="1"
flexGrow={1}
spellCheck={!disableSpellcheck}
style={{ resize: 'vertical' }}
{...field}
onKeyDown={onKeyDown}

View File

@ -90,6 +90,11 @@ export interface VirtualScrollerProps<K,V> {
* Callback to execute when finished loading from start
*/
onBottomLoaded?: () => void;
/*
* Callback to execute when finished loading from end
*/
onTopLoaded?: () => void;
/*
* equality function for the key type
*/
@ -413,6 +418,9 @@ export default class VirtualScroller<K,V> extends Component<VirtualScrollerProps
if(newer && this.props.onBottomLoaded) {
this.props.onBottomLoaded();
}
if(!newer && this.props.onTopLoaded) {
this.props.onTopLoaded();
}
}
};

View File

@ -40,7 +40,6 @@ const SEARCHED_CATEGORIES = [
'other',
'groups',
'subscriptions',
'apps'
];
const settingsSel = (s: SettingsState) => s.leap;
const CAT_LIMIT = 6;
@ -57,7 +56,6 @@ export function Omnibox(props: OmniboxProps): ReactElement {
const contactState = useContactState(state => state.contacts);
const notificationCount = useHarkState(state => state.notificationsCount);
const invites = useInviteState(state => state.invites);
const tiles = useLaunchState(state => state.tiles);
const [leapCursor, setLeapCursor] = useState('pointer');
const contacts = useMemo(() => {
@ -83,12 +81,11 @@ export function Omnibox(props: OmniboxProps): ReactElement {
return makeIndex(
contacts,
associations,
tiles,
selectedGroup,
groups,
leapConfig
);
}, [selectedGroup, leapConfig, contacts, associations, groups, tiles]);
}, [selectedGroup, leapConfig, contacts, associations, groups]);
const onOutsideClick = useCallback(() => {
props.show && props.toggle();

View File

@ -114,7 +114,6 @@ export function GroupSwitcher(props: {
width="100%"
alignItems="stretch"
>
{(props.baseUrl === '/~landscape/home') ?
<GroupSwitcherItem to="">
<Icon
mr={2}
@ -124,16 +123,6 @@ export function GroupSwitcher(props: {
/>
<Text>All Groups</Text>
</GroupSwitcherItem>
:
<GroupSwitcherItem to="/~landscape/home">
<Icon
mr={2}
color="gray"
display="block"
icon="Home"
/>
<Text>My Channels</Text>
</GroupSwitcherItem>}
<RecentGroups
recent={props.recentGroups}
/>