mirror of
https://github.com/luminous-devs/lume.git
synced 2024-10-02 18:00:47 +00:00
refactor text parser
This commit is contained in:
parent
fd5ecc18a9
commit
e218ebee89
@ -63,8 +63,10 @@
|
|||||||
"destr": "^2.0.2",
|
"destr": "^2.0.2",
|
||||||
"framer-motion": "^10.16.4",
|
"framer-motion": "^10.16.4",
|
||||||
"html-to-text": "^9.0.5",
|
"html-to-text": "^9.0.5",
|
||||||
|
"immer": "^10.0.3",
|
||||||
"light-bolt11-decoder": "^3.0.0",
|
"light-bolt11-decoder": "^3.0.0",
|
||||||
"lru-cache": "^10.0.1",
|
"lru-cache": "^10.0.1",
|
||||||
|
"markdown-to-jsx": "^7.3.2",
|
||||||
"media-chrome": "^1.4.5",
|
"media-chrome": "^1.4.5",
|
||||||
"million": "^2.6.4",
|
"million": "^2.6.4",
|
||||||
"minidenticons": "^4.2.0",
|
"minidenticons": "^4.2.0",
|
||||||
|
@ -140,12 +140,18 @@ dependencies:
|
|||||||
html-to-text:
|
html-to-text:
|
||||||
specifier: ^9.0.5
|
specifier: ^9.0.5
|
||||||
version: 9.0.5
|
version: 9.0.5
|
||||||
|
immer:
|
||||||
|
specifier: ^10.0.3
|
||||||
|
version: 10.0.3
|
||||||
light-bolt11-decoder:
|
light-bolt11-decoder:
|
||||||
specifier: ^3.0.0
|
specifier: ^3.0.0
|
||||||
version: 3.0.0
|
version: 3.0.0
|
||||||
lru-cache:
|
lru-cache:
|
||||||
specifier: ^10.0.1
|
specifier: ^10.0.1
|
||||||
version: 10.0.1
|
version: 10.0.1
|
||||||
|
markdown-to-jsx:
|
||||||
|
specifier: ^7.3.2
|
||||||
|
version: 7.3.2(react@18.2.0)
|
||||||
media-chrome:
|
media-chrome:
|
||||||
specifier: ^1.4.5
|
specifier: ^1.4.5
|
||||||
version: 1.4.5
|
version: 1.4.5
|
||||||
@ -193,7 +199,7 @@ dependencies:
|
|||||||
version: 1.1.1
|
version: 1.1.1
|
||||||
reactflow:
|
reactflow:
|
||||||
specifier: ^11.9.4
|
specifier: ^11.9.4
|
||||||
version: 11.9.4(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0)
|
version: 11.9.4(@types/react@18.2.33)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0)
|
||||||
rehype-external-links:
|
rehype-external-links:
|
||||||
specifier: ^3.0.0
|
specifier: ^3.0.0
|
||||||
version: 3.0.0
|
version: 3.0.0
|
||||||
@ -220,7 +226,7 @@ dependencies:
|
|||||||
version: 0.15.6(react-dom@18.2.0)(react@18.2.0)
|
version: 0.15.6(react-dom@18.2.0)(react@18.2.0)
|
||||||
zustand:
|
zustand:
|
||||||
specifier: ^4.4.4
|
specifier: ^4.4.4
|
||||||
version: 4.4.4(@types/react@18.2.33)(react@18.2.0)
|
version: 4.4.4(@types/react@18.2.33)(immer@10.0.3)(react@18.2.0)
|
||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@tailwindcss/typography':
|
'@tailwindcss/typography':
|
||||||
@ -1792,39 +1798,39 @@ packages:
|
|||||||
'@babel/runtime': 7.23.2
|
'@babel/runtime': 7.23.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@reactflow/background@11.3.4(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0):
|
/@reactflow/background@11.3.4(@types/react@18.2.33)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-bgwvqWxF09chwmdkyClpYEMaewBspdwjgLbbFlLf4SpWPFMYyuvCBQrcISsvy/EDEWO9i3Uj9ktgGAhvtSQsmA==}
|
resolution: {integrity: sha512-bgwvqWxF09chwmdkyClpYEMaewBspdwjgLbbFlLf4SpWPFMYyuvCBQrcISsvy/EDEWO9i3Uj9ktgGAhvtSQsmA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: '>=17'
|
react: '>=17'
|
||||||
react-dom: '>=17'
|
react-dom: '>=17'
|
||||||
dependencies:
|
dependencies:
|
||||||
'@reactflow/core': 11.9.4(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0)
|
'@reactflow/core': 11.9.4(@types/react@18.2.33)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0)
|
||||||
classcat: 5.0.4
|
classcat: 5.0.4
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-dom: 18.2.0(react@18.2.0)
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
zustand: 4.4.4(@types/react@18.2.33)(react@18.2.0)
|
zustand: 4.4.4(@types/react@18.2.33)(immer@10.0.3)(react@18.2.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@types/react'
|
- '@types/react'
|
||||||
- immer
|
- immer
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@reactflow/controls@11.2.4(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0):
|
/@reactflow/controls@11.2.4(@types/react@18.2.33)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-x6e5p9iHjC6gd+4SoZ3DOOp0F1MefGKQ8hT6yPVdqxfo1+rV2WhrWvrX/MCoEu12Dp7457LdLfa0giy3aho8tQ==}
|
resolution: {integrity: sha512-x6e5p9iHjC6gd+4SoZ3DOOp0F1MefGKQ8hT6yPVdqxfo1+rV2WhrWvrX/MCoEu12Dp7457LdLfa0giy3aho8tQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: '>=17'
|
react: '>=17'
|
||||||
react-dom: '>=17'
|
react-dom: '>=17'
|
||||||
dependencies:
|
dependencies:
|
||||||
'@reactflow/core': 11.9.4(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0)
|
'@reactflow/core': 11.9.4(@types/react@18.2.33)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0)
|
||||||
classcat: 5.0.4
|
classcat: 5.0.4
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-dom: 18.2.0(react@18.2.0)
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
zustand: 4.4.4(@types/react@18.2.33)(react@18.2.0)
|
zustand: 4.4.4(@types/react@18.2.33)(immer@10.0.3)(react@18.2.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@types/react'
|
- '@types/react'
|
||||||
- immer
|
- immer
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@reactflow/core@11.9.4(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0):
|
/@reactflow/core@11.9.4(@types/react@18.2.33)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-Ko7nKPOYalwDTTbRHi2+QXDiidSAcpUzGN3G+0B+QysLZkcaPCkpkMjjHiDC4c/Z1BJBzs1FRJg/T6BXaBnYkg==}
|
resolution: {integrity: sha512-Ko7nKPOYalwDTTbRHi2+QXDiidSAcpUzGN3G+0B+QysLZkcaPCkpkMjjHiDC4c/Z1BJBzs1FRJg/T6BXaBnYkg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: '>=17'
|
react: '>=17'
|
||||||
@ -1840,19 +1846,19 @@ packages:
|
|||||||
d3-zoom: 3.0.0
|
d3-zoom: 3.0.0
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-dom: 18.2.0(react@18.2.0)
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
zustand: 4.4.4(@types/react@18.2.33)(react@18.2.0)
|
zustand: 4.4.4(@types/react@18.2.33)(immer@10.0.3)(react@18.2.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@types/react'
|
- '@types/react'
|
||||||
- immer
|
- immer
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@reactflow/minimap@11.7.4(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0):
|
/@reactflow/minimap@11.7.4(@types/react@18.2.33)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-Jo1R+uDey9IV7O2s3m0gK2+cZpg9M8hq2EZJb3NGfOSzMAPhj3mby0fNJIgTzycreuht0TpA51c2YfjGI3YIOw==}
|
resolution: {integrity: sha512-Jo1R+uDey9IV7O2s3m0gK2+cZpg9M8hq2EZJb3NGfOSzMAPhj3mby0fNJIgTzycreuht0TpA51c2YfjGI3YIOw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: '>=17'
|
react: '>=17'
|
||||||
react-dom: '>=17'
|
react-dom: '>=17'
|
||||||
dependencies:
|
dependencies:
|
||||||
'@reactflow/core': 11.9.4(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0)
|
'@reactflow/core': 11.9.4(@types/react@18.2.33)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0)
|
||||||
'@types/d3-selection': 3.0.8
|
'@types/d3-selection': 3.0.8
|
||||||
'@types/d3-zoom': 3.0.6
|
'@types/d3-zoom': 3.0.6
|
||||||
classcat: 5.0.4
|
classcat: 5.0.4
|
||||||
@ -1860,41 +1866,41 @@ packages:
|
|||||||
d3-zoom: 3.0.0
|
d3-zoom: 3.0.0
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-dom: 18.2.0(react@18.2.0)
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
zustand: 4.4.4(@types/react@18.2.33)(react@18.2.0)
|
zustand: 4.4.4(@types/react@18.2.33)(immer@10.0.3)(react@18.2.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@types/react'
|
- '@types/react'
|
||||||
- immer
|
- immer
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@reactflow/node-resizer@2.2.4(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0):
|
/@reactflow/node-resizer@2.2.4(@types/react@18.2.33)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-+p271/hAsM5M1+RQTWW/02pbNkCHeGXwxGimIlL1tMIagyuko0NX2vOz2B8jxJnPKlF09Wj18BcXBNUm3nDcSg==}
|
resolution: {integrity: sha512-+p271/hAsM5M1+RQTWW/02pbNkCHeGXwxGimIlL1tMIagyuko0NX2vOz2B8jxJnPKlF09Wj18BcXBNUm3nDcSg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: '>=17'
|
react: '>=17'
|
||||||
react-dom: '>=17'
|
react-dom: '>=17'
|
||||||
dependencies:
|
dependencies:
|
||||||
'@reactflow/core': 11.9.4(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0)
|
'@reactflow/core': 11.9.4(@types/react@18.2.33)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0)
|
||||||
classcat: 5.0.4
|
classcat: 5.0.4
|
||||||
d3-drag: 3.0.0
|
d3-drag: 3.0.0
|
||||||
d3-selection: 3.0.0
|
d3-selection: 3.0.0
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-dom: 18.2.0(react@18.2.0)
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
zustand: 4.4.4(@types/react@18.2.33)(react@18.2.0)
|
zustand: 4.4.4(@types/react@18.2.33)(immer@10.0.3)(react@18.2.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@types/react'
|
- '@types/react'
|
||||||
- immer
|
- immer
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@reactflow/node-toolbar@1.3.4(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0):
|
/@reactflow/node-toolbar@1.3.4(@types/react@18.2.33)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-TfcmpXHRBb2mUfzKGjburiU6FWqRME9pPFs1OwIC1z5e9BjupQhNDEKEk8XHi7PKL/mAiDfwuGXaM1BVVFuPqw==}
|
resolution: {integrity: sha512-TfcmpXHRBb2mUfzKGjburiU6FWqRME9pPFs1OwIC1z5e9BjupQhNDEKEk8XHi7PKL/mAiDfwuGXaM1BVVFuPqw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: '>=17'
|
react: '>=17'
|
||||||
react-dom: '>=17'
|
react-dom: '>=17'
|
||||||
dependencies:
|
dependencies:
|
||||||
'@reactflow/core': 11.9.4(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0)
|
'@reactflow/core': 11.9.4(@types/react@18.2.33)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0)
|
||||||
classcat: 5.0.4
|
classcat: 5.0.4
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-dom: 18.2.0(react@18.2.0)
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
zustand: 4.4.4(@types/react@18.2.33)(react@18.2.0)
|
zustand: 4.4.4(@types/react@18.2.33)(immer@10.0.3)(react@18.2.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@types/react'
|
- '@types/react'
|
||||||
- immer
|
- immer
|
||||||
@ -4445,6 +4451,10 @@ packages:
|
|||||||
engines: {node: '>= 4'}
|
engines: {node: '>= 4'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/immer@10.0.3:
|
||||||
|
resolution: {integrity: sha512-pwupu3eWfouuaowscykeckFmVTpqbzW+rXFCX8rQLkZzM9ftBmU/++Ra+o+L27mz03zJTlyV4UUr+fdKNffo4A==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/import-fresh@3.3.0:
|
/import-fresh@3.3.0:
|
||||||
resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
|
resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@ -4922,6 +4932,15 @@ packages:
|
|||||||
resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==}
|
resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/markdown-to-jsx@7.3.2(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-B+28F5ucp83aQm+OxNrPkS8z0tMKaeHiy0lHJs3LqCyDQFtWuenaIrkaVTgAm1pf1AU85LXltva86hlaT17i8Q==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>= 0.14.0'
|
||||||
|
dependencies:
|
||||||
|
react: 18.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/mdast-util-find-and-replace@3.0.1:
|
/mdast-util-find-and-replace@3.0.1:
|
||||||
resolution: {integrity: sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==}
|
resolution: {integrity: sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -6122,18 +6141,18 @@ packages:
|
|||||||
loose-envify: 1.4.0
|
loose-envify: 1.4.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/reactflow@11.9.4(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0):
|
/reactflow@11.9.4(@types/react@18.2.33)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-IHAKBkJngNvU9y1vZ5Nw9rvA3Z+zc9geTgQQIi9qq9Y9knGLlDDr9KfsjbFMew9AycAAgVg8TvBEakF4IT5lqg==}
|
resolution: {integrity: sha512-IHAKBkJngNvU9y1vZ5Nw9rvA3Z+zc9geTgQQIi9qq9Y9knGLlDDr9KfsjbFMew9AycAAgVg8TvBEakF4IT5lqg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: '>=17'
|
react: '>=17'
|
||||||
react-dom: '>=17'
|
react-dom: '>=17'
|
||||||
dependencies:
|
dependencies:
|
||||||
'@reactflow/background': 11.3.4(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0)
|
'@reactflow/background': 11.3.4(@types/react@18.2.33)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0)
|
||||||
'@reactflow/controls': 11.2.4(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0)
|
'@reactflow/controls': 11.2.4(@types/react@18.2.33)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0)
|
||||||
'@reactflow/core': 11.9.4(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0)
|
'@reactflow/core': 11.9.4(@types/react@18.2.33)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0)
|
||||||
'@reactflow/minimap': 11.7.4(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0)
|
'@reactflow/minimap': 11.7.4(@types/react@18.2.33)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0)
|
||||||
'@reactflow/node-resizer': 2.2.4(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0)
|
'@reactflow/node-resizer': 2.2.4(@types/react@18.2.33)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0)
|
||||||
'@reactflow/node-toolbar': 1.3.4(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0)
|
'@reactflow/node-toolbar': 1.3.4(@types/react@18.2.33)(immer@10.0.3)(react-dom@18.2.0)(react@18.2.0)
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-dom: 18.2.0(react@18.2.0)
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@ -7117,7 +7136,7 @@ packages:
|
|||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/zustand@4.4.4(@types/react@18.2.33)(react@18.2.0):
|
/zustand@4.4.4(@types/react@18.2.33)(immer@10.0.3)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-5UTUIAiHMNf5+mFp7/AnzJXS7+XxktULFN0+D1sCiZWyX7ZG+AQpqs2qpYrynRij4QvoDdCD+U+bmg/cG3Ucxw==}
|
resolution: {integrity: sha512-5UTUIAiHMNf5+mFp7/AnzJXS7+XxktULFN0+D1sCiZWyX7ZG+AQpqs2qpYrynRij4QvoDdCD+U+bmg/cG3Ucxw==}
|
||||||
engines: {node: '>=12.7.0'}
|
engines: {node: '>=12.7.0'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -7133,6 +7152,7 @@ packages:
|
|||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/react': 18.2.33
|
'@types/react': 18.2.33
|
||||||
|
immer: 10.0.3
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
use-sync-external-store: 1.2.0(react@18.2.0)
|
use-sync-external-store: 1.2.0(react@18.2.0)
|
||||||
dev: false
|
dev: false
|
||||||
|
@ -1,6 +1,16 @@
|
|||||||
|
import Markdown from 'markdown-to-jsx';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
|
||||||
import { ImagePreview, LinkPreview, MentionNote, VideoPreview } from '@shared/notes';
|
import {
|
||||||
|
Boost,
|
||||||
|
Hashtag,
|
||||||
|
ImagePreview,
|
||||||
|
Invoice,
|
||||||
|
LinkPreview,
|
||||||
|
MentionNote,
|
||||||
|
MentionUser,
|
||||||
|
VideoPreview,
|
||||||
|
} from '@shared/notes';
|
||||||
|
|
||||||
import { parser } from '@utils/parser';
|
import { parser } from '@utils/parser';
|
||||||
|
|
||||||
@ -17,9 +27,30 @@ export function TextNote(props: { content?: string; truncate?: boolean }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="break-p prose prose-neutral max-w-none select-text whitespace-pre-line leading-normal dark:prose-invert prose-headings:mb-1 prose-headings:mt-3 prose-p:mb-0 prose-p:mt-0 prose-p:last:mb-1 prose-a:font-normal prose-a:text-blue-500 prose-blockquote:mb-1 prose-blockquote:mt-1 prose-blockquote:border-l-[2px] prose-blockquote:border-blue-500 prose-blockquote:pl-2 prose-pre:whitespace-pre-wrap prose-pre:bg-white/10 prose-ol:m-0 prose-ol:mb-1 prose-ul:mb-1 prose-ul:mt-1 prose-img:mb-2 prose-img:mt-3 prose-hr:mx-0 prose-hr:my-2 hover:prose-a:text-blue-600 prose-a:hover:underline">
|
<Markdown
|
||||||
|
options={{
|
||||||
|
overrides: {
|
||||||
|
Hashtag: {
|
||||||
|
component: Hashtag,
|
||||||
|
},
|
||||||
|
Boost: {
|
||||||
|
component: Boost,
|
||||||
|
},
|
||||||
|
MentionUser: {
|
||||||
|
component: MentionUser,
|
||||||
|
},
|
||||||
|
Invoice: {
|
||||||
|
component: Invoice,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
slugify: (str) => str,
|
||||||
|
forceBlock: true,
|
||||||
|
enforceAtxHeadings: true,
|
||||||
|
}}
|
||||||
|
className="break-p prose prose-neutral max-w-none select-text whitespace-pre-line leading-normal dark:prose-invert prose-headings:mb-1 prose-headings:mt-3 prose-p:mb-0 prose-p:mt-0 prose-p:last:mb-1 prose-a:font-normal prose-a:text-blue-500 prose-blockquote:mb-1 prose-blockquote:mt-1 prose-blockquote:border-l-[2px] prose-blockquote:border-blue-500 prose-blockquote:pl-2 prose-pre:whitespace-pre-wrap prose-pre:bg-white/10 prose-ol:m-0 prose-ol:mb-1 prose-ul:mb-1 prose-ul:mt-1 prose-img:mb-2 prose-img:mt-3 prose-hr:mx-0 prose-hr:my-2 hover:prose-a:text-blue-600 prose-a:hover:underline"
|
||||||
|
>
|
||||||
{richContent.parsed}
|
{richContent.parsed}
|
||||||
</div>
|
</Markdown>
|
||||||
{richContent.images.length ? <ImagePreview urls={richContent.images} /> : null}
|
{richContent.images.length ? <ImagePreview urls={richContent.images} /> : null}
|
||||||
{richContent.videos.length ? <VideoPreview urls={richContent.videos} /> : null}
|
{richContent.videos.length ? <VideoPreview urls={richContent.videos} /> : null}
|
||||||
{richContent.links.length ? <LinkPreview urls={richContent.links} /> : null}
|
{richContent.links.length ? <LinkPreview urls={richContent.links} /> : null}
|
||||||
|
@ -75,13 +75,14 @@ export const MentionNote = memo(function MentionNote({ id }: { id: string }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/interactive-supports-focus
|
||||||
type="button"
|
<div
|
||||||
|
role="button"
|
||||||
onClick={(e) => openThread(e, id)}
|
onClick={(e) => openThread(e, id)}
|
||||||
className="mt-3 cursor-default rounded-lg border border-neutral-300 bg-neutral-200 p-3 dark:border-neutral-700 dark:bg-neutral-800"
|
className="mt-3 cursor-default rounded-lg border border-neutral-300 bg-neutral-200 p-3 dark:border-neutral-700 dark:bg-neutral-800"
|
||||||
>
|
>
|
||||||
<User pubkey={data.pubkey} time={data.created_at} variant="mention" />
|
<User pubkey={data.pubkey} time={data.created_at} variant="mention" />
|
||||||
<div className="mt-1 text-left">{renderKind(data)}</div>
|
<div className="mt-1 text-left">{renderKind(data)}</div>
|
||||||
</button>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -124,7 +124,13 @@ export function NewsfeedWidget() {
|
|||||||
sub(
|
sub(
|
||||||
filter,
|
filter,
|
||||||
async (event) => {
|
async (event) => {
|
||||||
queryClient.setQueryData(['newsfeed'], (old: NDKEvent[]) => [event, ...old]);
|
queryClient.setQueryData(
|
||||||
|
['newsfeed'],
|
||||||
|
(prev: { pageParams: number; pages: Array<NDKEvent[]> }) => ({
|
||||||
|
...prev,
|
||||||
|
pages: [[event], ...prev.pages],
|
||||||
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
'newsfeed'
|
'newsfeed'
|
||||||
|
@ -77,10 +77,13 @@ export function NotificationWidget() {
|
|||||||
sub(
|
sub(
|
||||||
filter,
|
filter,
|
||||||
async (event) => {
|
async (event) => {
|
||||||
queryClient.setQueryData(['notification'], (old: NDKEvent[]) => [
|
queryClient.setQueryData(
|
||||||
event,
|
['notification'],
|
||||||
...old,
|
(prev: { pageParams: number; pages: Array<NDKEvent[]> }) => ({
|
||||||
]);
|
...prev,
|
||||||
|
pages: [[event], ...prev.pages],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const user = ndk.getUser({ hexpubkey: event.pubkey });
|
const user = ndk.getUser({ hexpubkey: event.pubkey });
|
||||||
await user.fetchProfile();
|
await user.fetchProfile();
|
||||||
|
132
src/utils/parser.ts
Normal file
132
src/utils/parser.ts
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
import { nip19 } from 'nostr-tools';
|
||||||
|
import {
|
||||||
|
AddressPointer,
|
||||||
|
EventPointer,
|
||||||
|
ProfilePointer,
|
||||||
|
} from 'nostr-tools/lib/types/nip19';
|
||||||
|
|
||||||
|
import { RichContent } from '@utils/types';
|
||||||
|
|
||||||
|
function isURL(string: string) {
|
||||||
|
try {
|
||||||
|
const url = new URL(string);
|
||||||
|
if (url.protocol.length > 0) {
|
||||||
|
if (url.protocol === 'https:' || url.protocol === 'http:') {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parser(content: string) {
|
||||||
|
const richContent: RichContent = {
|
||||||
|
parsed: null,
|
||||||
|
images: [],
|
||||||
|
videos: [],
|
||||||
|
links: [],
|
||||||
|
notes: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const parsed = content
|
||||||
|
.trim()
|
||||||
|
.split(/(\s+)/)
|
||||||
|
.map((word) => {
|
||||||
|
// url
|
||||||
|
if (isURL(word)) {
|
||||||
|
const url = new URL(word);
|
||||||
|
url.search = '';
|
||||||
|
|
||||||
|
if (url.pathname.match(/\.(jpg|jpeg|gif|png|webp|avif)$/)) {
|
||||||
|
// image url
|
||||||
|
richContent.images.push(word);
|
||||||
|
// remove url from original content
|
||||||
|
return word.replace(word, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url.pathname.match(/\.(mp4|mov|webm|wmv|flv|mts|avi|ogv|mkv|mp3|m3u8)$/)) {
|
||||||
|
// video url
|
||||||
|
richContent.videos.push(word);
|
||||||
|
// remove url from original content
|
||||||
|
return word.replace(word, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
// normal url
|
||||||
|
if (richContent.links.length < 1) {
|
||||||
|
richContent.links.push(url.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashtag
|
||||||
|
if (word.startsWith('#') && word.length > 1) {
|
||||||
|
return word.replace(word, `<Hashtag tag='${word}' />`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// boost
|
||||||
|
if (word.startsWith('$prism') && word.length > 1) {
|
||||||
|
return word.replace(word, `<Boost boost='${word}' />`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// nostr account references (depreciated)
|
||||||
|
if (word.startsWith('@npub1')) {
|
||||||
|
const npub = word.replace('@', '').replace(/[^a-zA-Z0-9 ]/g, '');
|
||||||
|
return word.replace(
|
||||||
|
word,
|
||||||
|
`<MentionUser pubkey='${nip19.decode(npub).data as string}' />`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// nostr account references
|
||||||
|
if (word.startsWith('nostr:npub1') || word.startsWith('npub1')) {
|
||||||
|
const npub = word.replace('nostr:', '').replace(/[^a-zA-Z0-9 ]/g, '');
|
||||||
|
return word.replace(
|
||||||
|
word,
|
||||||
|
`<MentionUser pubkey='${nip19.decode(npub).data as string}' />`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// nostr profile references
|
||||||
|
if (word.startsWith('nostr:nprofile1') || word.startsWith('nprofile1')) {
|
||||||
|
const nprofile = word.replace('nostr:', '').replace(/[^a-zA-Z0-9 ]/g, '');
|
||||||
|
const decoded = nip19.decode(nprofile).data as ProfilePointer;
|
||||||
|
return word.replace(word, `<MentionUser pubkey='${decoded.pubkey}' />`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// nostr address references
|
||||||
|
if (word.startsWith('nostr:naddr1') || word.startsWith('naddr1')) {
|
||||||
|
const naddr = word.replace('nostr:', '').replace(/[^a-zA-Z0-9 ]/g, '');
|
||||||
|
const decoded = nip19.decode(naddr).data as AddressPointer;
|
||||||
|
return word.replace(word, `<MentionUser pubkey='${decoded.pubkey}' />`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// lightning invoice
|
||||||
|
if (word.startsWith('lnbc') && word.length > 60) {
|
||||||
|
return word.replace(word, `<Invoice invoice='${word}' />`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// nostr note references
|
||||||
|
if (word.startsWith('nostr:note1') || word.startsWith('note1')) {
|
||||||
|
const note = word.replace('nostr:', '').replace(/[^a-zA-Z0-9 ]/g, '');
|
||||||
|
richContent.notes.push(nip19.decode(note).data as string);
|
||||||
|
return word.replace(word, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
// nostr event references
|
||||||
|
if (word.startsWith('nostr:nevent1') || word.startsWith('nevent1')) {
|
||||||
|
const nevent = word.replace('nostr:', '').replace(/[^a-zA-Z0-9 ]/g, '');
|
||||||
|
const decoded = nip19.decode(nevent).data as EventPointer;
|
||||||
|
richContent.notes.push(decoded.id);
|
||||||
|
return word.replace(word, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
return word;
|
||||||
|
});
|
||||||
|
|
||||||
|
// update content with parsed version
|
||||||
|
richContent.parsed = parsed.join(' ').trim();
|
||||||
|
return richContent;
|
||||||
|
}
|
@ -1,173 +0,0 @@
|
|||||||
import { nip19 } from 'nostr-tools';
|
|
||||||
import {
|
|
||||||
AddressPointer,
|
|
||||||
EventPointer,
|
|
||||||
ProfilePointer,
|
|
||||||
} from 'nostr-tools/lib/types/nip19';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import reactStringReplace from 'react-string-replace';
|
|
||||||
|
|
||||||
import { Boost, Hashtag, Invoice, MentionUser } from '@shared/notes';
|
|
||||||
|
|
||||||
import { RichContent } from '@utils/types';
|
|
||||||
|
|
||||||
function isURL(string: string) {
|
|
||||||
try {
|
|
||||||
const url = new URL(string);
|
|
||||||
if (url.protocol.length > 0) {
|
|
||||||
if (url.protocol === 'https:' || url.protocol === 'http:') {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} catch (e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function parser(eventContent: string) {
|
|
||||||
const content: RichContent = {
|
|
||||||
parsed: null,
|
|
||||||
images: [],
|
|
||||||
videos: [],
|
|
||||||
links: [],
|
|
||||||
notes: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
const parsed = eventContent.split(/\s/gm).map((word) => {
|
|
||||||
// nostr note references
|
|
||||||
if (word.startsWith('nostr:note1') || word.startsWith('note1')) {
|
|
||||||
const note = word.replace('nostr:', '').replace(/[^a-zA-Z0-9 ]/g, '');
|
|
||||||
content.notes.push(nip19.decode(note).data as string);
|
|
||||||
return word.replace(word, ' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
// nostr event references
|
|
||||||
if (word.startsWith('nostr:nevent1') || word.startsWith('nevent1')) {
|
|
||||||
const nevent = word.replace('nostr:', '').replace(/[^a-zA-Z0-9 ]/g, '');
|
|
||||||
const decoded = nip19.decode(nevent).data as EventPointer;
|
|
||||||
content.notes.push(decoded.id);
|
|
||||||
return word.replace(word, ' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
// url
|
|
||||||
if (isURL(word)) {
|
|
||||||
const url = new URL(word);
|
|
||||||
url.search = '';
|
|
||||||
|
|
||||||
if (url.pathname.match(/\.(jpg|jpeg|gif|png|webp|avif)$/)) {
|
|
||||||
// image url
|
|
||||||
content.images.push(word);
|
|
||||||
// remove url from original content
|
|
||||||
return word.replace(word, ' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (url.pathname.match(/\.(mp4|mov|webm|wmv|flv|mts|avi|ogv|mkv|mp3|m3u8)$/)) {
|
|
||||||
// video url
|
|
||||||
content.videos.push(word);
|
|
||||||
// remove url from original content
|
|
||||||
return word.replace(word, ' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
// normal url
|
|
||||||
if (content.links.length < 1) {
|
|
||||||
content.links.push(url.toString());
|
|
||||||
return word.replace(word, ' ');
|
|
||||||
} else {
|
|
||||||
return reactStringReplace(word, word, (match, i) => (
|
|
||||||
<>
|
|
||||||
{' '}
|
|
||||||
<Link key={match + i} to={word} target="_blank" rel="noreferrer">
|
|
||||||
{word}
|
|
||||||
</Link>{' '}
|
|
||||||
</>
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// hashtag
|
|
||||||
if (word.startsWith('#') && word.length > 1) {
|
|
||||||
return reactStringReplace(word, word, (match, i) => (
|
|
||||||
<>
|
|
||||||
{' '}
|
|
||||||
<Hashtag key={match + i} tag={match} />{' '}
|
|
||||||
</>
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// boost
|
|
||||||
if (word.startsWith('$prism') && word.length > 1) {
|
|
||||||
return reactStringReplace(word, word, (match, i) => (
|
|
||||||
<>
|
|
||||||
{' '}
|
|
||||||
<Boost key={match + i} boost={match} />{' '}
|
|
||||||
</>
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// nostr account references (depreciated)
|
|
||||||
if (word.startsWith('@npub1')) {
|
|
||||||
const npub = word.replace('@', '').replace(/[^a-zA-Z0-9 ]/g, '');
|
|
||||||
return reactStringReplace(word, word, (match, i) => (
|
|
||||||
<>
|
|
||||||
{' '}
|
|
||||||
<MentionUser key={match + i} pubkey={nip19.decode(npub).data as string} />{' '}
|
|
||||||
</>
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// nostr account references
|
|
||||||
if (word.startsWith('nostr:npub1') || word.startsWith('npub1')) {
|
|
||||||
const npub = word.replace('nostr:', '').replace(/[^a-zA-Z0-9 ]/g, '');
|
|
||||||
return reactStringReplace(word, word, (match, i) => (
|
|
||||||
<>
|
|
||||||
{' '}
|
|
||||||
<MentionUser key={match + i} pubkey={nip19.decode(npub).data as string} />{' '}
|
|
||||||
</>
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// nostr profile references
|
|
||||||
if (word.startsWith('nostr:nprofile1') || word.startsWith('nprofile1')) {
|
|
||||||
const nprofile = word.replace('nostr:', '').replace(/[^a-zA-Z0-9 ]/g, '');
|
|
||||||
const decoded = nip19.decode(nprofile).data as ProfilePointer;
|
|
||||||
return reactStringReplace(word, word, (match, i) => (
|
|
||||||
<>
|
|
||||||
{' '}
|
|
||||||
<MentionUser key={match + i} pubkey={decoded.pubkey} />{' '}
|
|
||||||
</>
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// nostr address references
|
|
||||||
if (word.startsWith('nostr:naddr1') || word.startsWith('naddr1')) {
|
|
||||||
const naddr = word.replace('nostr:', '').replace(/[^a-zA-Z0-9 ]/g, '');
|
|
||||||
const decoded = nip19.decode(naddr).data as AddressPointer;
|
|
||||||
return reactStringReplace(word, word, (match, i) => (
|
|
||||||
<>
|
|
||||||
{' '}
|
|
||||||
<MentionUser key={match + i} pubkey={decoded.pubkey} />{' '}
|
|
||||||
</>
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// lightning invoice
|
|
||||||
if (word.startsWith('lnbc') && word.length > 60) {
|
|
||||||
return reactStringReplace(word, word, (match, i) => (
|
|
||||||
<>
|
|
||||||
{' '}
|
|
||||||
<Invoice key={match + i} invoice={word} />{' '}
|
|
||||||
</>
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// normal word
|
|
||||||
return ' ' + word + ' ';
|
|
||||||
});
|
|
||||||
|
|
||||||
// update content with parsed version
|
|
||||||
content.parsed = parsed;
|
|
||||||
return content;
|
|
||||||
}
|
|
3
src/utils/types.d.ts
vendored
3
src/utils/types.d.ts
vendored
@ -1,9 +1,8 @@
|
|||||||
import { type NDKEvent, type NDKUserProfile } from '@nostr-dev-kit/ndk';
|
import { type NDKEvent, type NDKUserProfile } from '@nostr-dev-kit/ndk';
|
||||||
import { type Response } from '@tauri-apps/plugin-http';
|
import { type Response } from '@tauri-apps/plugin-http';
|
||||||
import { ReactNode } from 'react';
|
|
||||||
|
|
||||||
export interface RichContent {
|
export interface RichContent {
|
||||||
parsed: string | ReactNode[];
|
parsed: string;
|
||||||
images: string[];
|
images: string[];
|
||||||
videos: string[];
|
videos: string[];
|
||||||
links: string[];
|
links: string[];
|
||||||
|
Loading…
Reference in New Issue
Block a user