Skip to content

Commit

Permalink
Add tags and mute words (#2968)
Browse files Browse the repository at this point in the history
* Add bare minimum hashtags support (#2804)

* Add bare minimum hashtags support

As atproto/api already parses hashtags, this is as simple as hooking it
up like link segments.

This is "bare minimum" because:

- Opening hashtag "#foo" is actually just a search for "foo" right now
  to work around #2491.
- There is no integration in the composer. This hasn't stopped people
  from using hashtags already, and can be added later.
- This change itself only had to hook things up - thank you for having
  already put the hashtag parsing in place.

* Remove workaround for hash search not working now that it's fixed

* Add RichTextTag and TagMenu

* Sketch

* Remove hackfix

* Some cleanup

* Sketch web

* Mobile design

* Mobile handling of tags search

* Web only

* Fix navigation woes

* Use new callback

* Hook it up

* Integrate muted tags

* Fix dropdown styles

* Type error

* Use close callback

* Fix styles

* Cleanup, install latest sdk

* Quick muted words screen

* Targets

* Dir structure

* Icons, list view

* Move to dialog

* Add removal confirmation

* Swap copy

* Improve checkboxees

* Update matching, add tests

* Moderate embeds

* Create global dialogs concept again to prevent flashing

* Add access from moderation screen

* Highlight tags on native

* Add web highlighting

* Add close to web modal

* Adjust close color

* Rename toggles and adjust logic

* Icon update

* Load states

* Improve regex

* Improve regex

* Improve regex

* Revert link test

* Hyphenated words

* Improve matching

* Enhance

* Some tweaks

* Muted words modal changes

* Handle invalid handles, handle long tags

* Remove main regex

* Better test

* Space/punct check drop to includes

* Lowercase post text before comparison

* Add better real world test case

---------

Co-authored-by: Kisaragi Hiu <mail@kisaragi-hiu.com>
  • Loading branch information
estrattonbailey and kisaragi-hiu authored Feb 27, 2024
1 parent c858292 commit 58aaad7
Show file tree
Hide file tree
Showing 49 changed files with 1,983 additions and 39 deletions.
1 change: 1 addition & 0 deletions assets/icons/checkThick_stroke2_corner0_rounded.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assets/icons/clipboard_stroke2_corner2_rounded.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assets/icons/magnifyingGlass2_stroke2_corner0_rounded.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assets/icons/mute_stroke2_corner0_rounded.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assets/icons/pageText_stroke2_corner0_rounded.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions bskyweb/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,11 @@
[data-tooltip]:hover::before {
display:block;
}

/* NativeDropdown component */
.nativeDropdown-item:focus {
outline: none;
}
</style>
{% include "scripts.html" %}
<link rel="apple-touch-icon" sizes="180x180" href="/static/apple-touch-icon.png">
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"nuke": "rm -rf ./node_modules && rm -rf ./ios && rm -rf ./android"
},
"dependencies": {
"@atproto/api": "^0.9.5",
"@atproto/api": "^0.10.0",
"@bam.tech/react-native-image-resizer": "^3.0.4",
"@braintree/sanitize-url": "^6.0.2",
"@emoji-mart/react": "^1.1.1",
Expand Down
3 changes: 2 additions & 1 deletion src/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,8 @@ const LINKING = {
},
])
} else {
return buildStateObject('Flat', name, params)
const res = buildStateObject('Flat', name, params)
return res
}
},
}
Expand Down
4 changes: 4 additions & 0 deletions src/alf/atoms.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {web, native} from '#/alf/util/platform'
import * as tokens from '#/alf/tokens'

export const atoms = {
Expand Down Expand Up @@ -113,6 +114,9 @@ export const atoms = {
flex_wrap: {
flexWrap: 'wrap',
},
flex_0: {
flex: web('0 0 auto') || (native(0) as number),
},
flex_1: {
flex: 1,
},
Expand Down
2 changes: 1 addition & 1 deletion src/components/Dialog/index.web.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ export function Close() {
<Button
size="small"
variant="ghost"
color="primary"
color="secondary"
shape="round"
onPress={close}
label={_(msg`Close active dialog`)}>
Expand Down
103 changes: 102 additions & 1 deletion src/components/RichText.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import React from 'react'
import {RichText as RichTextAPI, AppBskyRichtextFacet} from '@atproto/api'
import {useLingui} from '@lingui/react'
import {msg} from '@lingui/macro'

import {atoms as a, TextStyleProp, flatten} from '#/alf'
import {atoms as a, TextStyleProp, flatten, useTheme, web, native} from '#/alf'
import {InlineLink} from '#/components/Link'
import {Text, TextProps} from '#/components/Typography'
import {toShortUrl} from 'lib/strings/url-helpers'
import {getAgent} from '#/state/session'
import {TagMenu, useTagMenuControl} from '#/components/TagMenu'
import {isNative} from '#/platform/detection'
import {useInteractionState} from '#/components/hooks/useInteractionState'

const WORD_WRAP = {wordWrap: 1}

Expand All @@ -17,13 +22,17 @@ export function RichText({
disableLinks,
resolveFacets = false,
selectable,
enableTags = false,
authorHandle,
}: TextStyleProp &
Pick<TextProps, 'selectable'> & {
value: RichTextAPI | string
testID?: string
numberOfLines?: number
disableLinks?: boolean
resolveFacets?: boolean
enableTags?: boolean
authorHandle?: string
}) {
const detected = React.useRef(false)
const [richText, setRichText] = React.useState<RichTextAPI>(() =>
Expand Down Expand Up @@ -85,6 +94,7 @@ export function RichText({
for (const segment of richText.segments()) {
const link = segment.link
const mention = segment.mention
const tag = segment.tag
if (
mention &&
AppBskyRichtextFacet.validateMention(mention).success &&
Expand Down Expand Up @@ -118,6 +128,21 @@ export function RichText({
</InlineLink>,
)
}
} else if (
!disableLinks &&
enableTags &&
tag &&
AppBskyRichtextFacet.validateTag(tag).success
) {
els.push(
<RichTextTag
key={key}
text={segment.text}
style={styles}
selectable={selectable}
authorHandle={authorHandle}
/>,
)
} else {
els.push(segment.text)
}
Expand All @@ -136,3 +161,79 @@ export function RichText({
</Text>
)
}

function RichTextTag({
text: tag,
style,
selectable,
authorHandle,
}: {
text: string
selectable?: boolean
authorHandle?: string
} & TextStyleProp) {
const t = useTheme()
const {_} = useLingui()
const control = useTagMenuControl()
const {
state: hovered,
onIn: onHoverIn,
onOut: onHoverOut,
} = useInteractionState()
const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState()
const {
state: pressed,
onIn: onPressIn,
onOut: onPressOut,
} = useInteractionState()

const open = React.useCallback(() => {
control.open()
}, [control])

/*
* N.B. On web, this is wrapped in another pressable comopnent with a11y
* labels, etc. That's why only some of these props are applied here.
*/

return (
<React.Fragment>
<TagMenu control={control} tag={tag} authorHandle={authorHandle}>
<Text
selectable={selectable}
{...native({
accessibilityLabel: _(msg`Hashtag: ${tag}`),
accessibilityHint: _(msg`Click here to open tag menu for ${tag}`),
accessibilityRole: isNative ? 'button' : undefined,
onPress: open,
onPressIn: onPressIn,
onPressOut: onPressOut,
})}
{...web({
onMouseEnter: onHoverIn,
onMouseLeave: onHoverOut,
})}
// @ts-ignore
onFocus={onFocus}
onBlur={onBlur}
style={[
style,
{
pointerEvents: 'auto',
color: t.palette.primary_500,
},
web({
cursor: 'pointer',
}),
(hovered || focused || pressed) && {
...web({outline: 0}),
textDecorationLine: 'underline',
textDecorationColor: t.palette.primary_500,
},
]}>
{tag}
</Text>
</TagMenu>
</React.Fragment>
)
}
Loading

0 comments on commit 58aaad7

Please sign in to comment.