Merge branch 'dev' of github.com:Discours/discoursio-webapp into dev

This commit is contained in:
Untone 2024-03-19 10:15:40 +03:00
commit d9b3d18e95
12 changed files with 110 additions and 50 deletions

6
package-lock.json generated
View File

@ -11,6 +11,7 @@
"license": "MIT",
"dependencies": {
"form-data": "4.0.0",
"idb": "8.0.0",
"mailgun.js": "10.1.0"
},
"devDependencies": {
@ -7231,6 +7232,11 @@
"node": ">=0.10.0"
}
},
"node_modules/idb": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/idb/-/idb-8.0.0.tgz",
"integrity": "sha512-l//qvlAKGmQO31Qn7xdzagVPPaHTxXx199MhrAFuVBTPqydcPYBWjkrbv4Y0ktB+GmWOiwHl237UUOrLmQxLvw=="
},
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",

View File

@ -29,6 +29,7 @@
},
"dependencies": {
"form-data": "4.0.0",
"idb": "8.0.0",
"mailgun.js": "10.1.0"
},
"devDependencies": {

View File

@ -14,6 +14,7 @@ import { MediaQueryProvider } from '../context/mediaQuery'
import { NotificationsProvider } from '../context/notifications'
import { SessionProvider } from '../context/session'
import { SnackbarProvider } from '../context/snackbar'
import { TopicsProvider } from '../context/topics'
import { DiscussionRulesPage } from '../pages/about/discussionRules.page'
import { DogmaPage } from '../pages/about/dogma.page'
import { GuidePage } from '../pages/about/guide.page'
@ -116,21 +117,23 @@ export const App = (props: Props) => {
<LocalizeProvider>
<MediaQueryProvider>
<SnackbarProvider>
<ConfirmProvider>
<SessionProvider onStateChangeCallback={console.log}>
<FollowingProvider>
<ConnectProvider>
<NotificationsProvider>
<EditorProvider>
<InboxProvider>
<Dynamic component={pageComponent()} {...props} />
</InboxProvider>
</EditorProvider>
</NotificationsProvider>
</ConnectProvider>
</FollowingProvider>
</SessionProvider>
</ConfirmProvider>
<TopicsProvider>
<ConfirmProvider>
<SessionProvider onStateChangeCallback={console.log}>
<FollowingProvider>
<ConnectProvider>
<NotificationsProvider>
<EditorProvider>
<InboxProvider>
<Dynamic component={pageComponent()} {...props} />
</InboxProvider>
</EditorProvider>
</NotificationsProvider>
</ConnectProvider>
</FollowingProvider>
</SessionProvider>
</ConfirmProvider>
</TopicsProvider>
</SnackbarProvider>
</MediaQueryProvider>
</LocalizeProvider>

View File

@ -6,12 +6,10 @@ import { For, Show, createEffect, createSignal, onCleanup, onMount } from 'solid
import { useLocalize } from '../../../context/localize'
import { useSession } from '../../../context/session'
import { apiClient } from '../../../graphql/client/core'
import { ROUTES, router, useRouter } from '../../../stores/router'
import { useModalStore } from '../../../stores/ui'
import { getDescription } from '../../../utils/meta'
import { SharePopup, getShareUrl } from '../../Article/SharePopup'
import { RANDOM_TOPICS_COUNT } from '../../Views/Home'
import { Icon } from '../../_shared/Icon'
import { Subscribe } from '../../_shared/Subscribe'
import { AuthModal } from '../AuthModal'
@ -23,6 +21,8 @@ import { Snackbar } from '../Snackbar'
import { Link } from './Link'
import { useTopics } from '../../../context/topics'
import { getRandomTopicsFromArray } from '../../../utils/getRandomTopicsFromArray'
import styles from './Header.module.scss'
type Props = {
@ -48,6 +48,7 @@ export const Header = (props: Props) => {
const { page } = useRouter()
const { requireAuthentication } = useSession()
const { searchParams } = useRouter<HeaderSearchParams>()
const { topics } = useTopics()
const [randomTopics, setRandomTopics] = createSignal([])
const [getIsScrollingBottom, setIsScrollingBottom] = createSignal(false)
const [getIsScrolled, setIsScrolled] = createSignal(false)
@ -58,6 +59,7 @@ export const Header = (props: Props) => {
const [isTopicsVisible, setIsTopicsVisible] = createSignal(false)
const [isZineVisible, setIsZineVisible] = createSignal(false)
const [isFeedVisible, setIsFeedVisible] = createSignal(false)
const toggleFixed = () => setFixed(!fixed())
const tag = (topic: Topic) =>
@ -65,6 +67,10 @@ export const Header = (props: Props) => {
let windowScrollTop = 0
createEffect(() => {
setRandomTopics(getRandomTopicsFromArray(topics()))
})
createEffect(() => {
const mainContent = document.querySelector<HTMLDivElement>('.main-content')
@ -141,11 +147,6 @@ export const Header = (props: Props) => {
}, time)
}
onMount(async () => {
const topics = await apiClient.getRandomTopics({ amount: RANDOM_TOPICS_COUNT })
setRandomTopics(topics)
})
const handleToggleMenuByLink = (event: MouseEvent, route: keyof typeof ROUTES) => {
if (!fixed()) {
return

View File

@ -123,7 +123,7 @@ export const ProfileSettings = () => {
setIsUserpicUpdating(true)
const result = await handleImageUpload(uploadFile)
updateFormField('userpic', result.url)
updateFormField('pic', result.url)
setUserpicFile(null)
setIsUserpicUpdating(false)

View File

@ -24,8 +24,8 @@ type Props = {
layout: LayoutType
}
export const PRERENDERED_ARTICLES_COUNT = 24
const LOAD_MORE_PAGE_SIZE = 16
export const PRERENDERED_ARTICLES_COUNT = 36
const LOAD_MORE_PAGE_SIZE = 12
export const Expo = (props: Props) => {
const [isLoaded, setIsLoaded] = createSignal<boolean>(Boolean(props.shouts))

View File

@ -64,18 +64,8 @@ export const HomeView = (props: Props) => {
limit: CLIENT_LOAD_ARTICLES_COUNT,
offset: sortedArticles().length,
})
setIsLoadMoreButtonVisible(hasMore)
}
const result = await apiClient.getRandomTopicShouts(RANDOM_TOPIC_SHOUTS_COUNT)
if (!result || result.error) console.warn('[apiClient.getRandomTopicShouts] failed')
batch(() => {
if (!result?.error) {
if (result?.topic) setRandomTopic(result.topic)
if (result?.shouts) setRandomTopicArticles(result.shouts)
}
})
})
const loadMore = async () => {

View File

@ -54,18 +54,12 @@ export const ProfileFormProvider = (props: { children: JSX.Element }) => {
const updateFormField = (fieldName: string, value: string, remove?: boolean) => {
if (fieldName === 'links') {
if (remove) {
setForm(
'links',
form.links.filter((item) => item !== value),
)
} else {
setForm((prev) => ({ ...prev, links: [...prev.links, value] }))
}
} else {
setForm({
[fieldName]: value,
setForm((prev) => {
const updatedLinks = remove ? prev.links.filter((item) => item !== value) : [...prev.links, value]
return { ...prev, links: updatedLinks }
})
} else {
setForm((prev) => ({ ...prev, [fieldName]: value }))
}
}

59
src/context/topics.tsx Normal file
View File

@ -0,0 +1,59 @@
import { openDB } from 'idb'
import { Accessor, JSX, createContext, createSignal, onMount, useContext } from 'solid-js'
import { apiClient } from '../graphql/client/core'
import { Topic } from '../graphql/schema/core.gen'
type TopicsContextType = {
topics: Accessor<Topic[]>
}
const TopicsContext = createContext<TopicsContextType>()
export function useTopics() {
return useContext(TopicsContext)
}
const DB_NAME = 'discourseAppDB'
const DB_VERSION = 1
const STORE_NAME = 'topics'
const setupIndexedDB = async () => {
return await openDB(DB_NAME, DB_VERSION, {
upgrade(db) {
if (!db.objectStoreNames.contains(STORE_NAME)) {
db.createObjectStore(STORE_NAME, { keyPath: 'id' })
}
},
})
}
const getTopicsFromIndexedDB = async (db) => {
const tx = db.transaction(STORE_NAME, 'readonly')
const store = tx.objectStore(STORE_NAME)
return store.getAll()
}
const saveTopicsToIndexedDB = async (db, topics) => {
const tx = db.transaction(STORE_NAME, 'readwrite')
const store = tx.objectStore(STORE_NAME)
for (const topic of topics) {
await store.put(topic)
}
await tx.done
}
export const TopicsProvider = (props: { children: JSX.Element }) => {
const [randomTopics, setRandomTopics] = createSignal<Topic[]>([])
onMount(async () => {
const db = await setupIndexedDB()
let topics = await getTopicsFromIndexedDB(db)
if (topics.length === 0) {
topics = await apiClient.getAllTopics()
await saveTopicsToIndexedDB(db, topics)
}
setRandomTopics(topics)
})
const value: TopicsContextType = { topics: randomTopics }
return <TopicsContext.Provider value={value}>{props.children}</TopicsContext.Provider>
}

View File

@ -182,7 +182,7 @@ export const apiClient = {
createReaction: async (input: ReactionInput) => {
const response = await apiClient.private.mutation(reactionCreate, { reaction: input }).toPromise()
console.debug('[graphql.client.core] createReaction:', response)
return response.data.create_reaction.reaction
return response.data.create_reaction
},
destroyReaction: async (reaction_id: number) => {
const response = await apiClient.private.mutation(reactionDestroy, { reaction_id }).toPromise()
@ -192,7 +192,7 @@ export const apiClient = {
updateReaction: async (reaction: ReactionInput) => {
const response = await apiClient.private.mutation(reactionUpdate, { reaction }).toPromise()
console.debug('[graphql.client.core] updateReaction:', response)
return response.data.update_reaction.reaction
return response.data.update_reaction
},
loadAuthorsBy: async (args: QueryLoad_Authors_ByArgs) => {
const resp = await publicGraphQLClient.query(authorsLoadBy, args).toPromise()

View File

@ -131,9 +131,8 @@ export const loadShout = async (slug: string): Promise<void> => {
export const loadShouts = async (
options: LoadShoutsOptions,
): Promise<{ hasMore: boolean; newShouts: Shout[] }> => {
options.limit += 1
const newShouts = await apiClient.getShouts(options)
const hasMore = newShouts?.length === options.limit + 1
const hasMore = newShouts?.length !== options.limit + 1 && newShouts?.length !== 0
if (hasMore) {
newShouts.splice(-1)

View File

@ -0,0 +1,7 @@
import { RANDOM_TOPICS_COUNT } from '../components/Views/Home'
import { Topic } from '../graphql/schema/core.gen'
export const getRandomTopicsFromArray = (topics: Topic[], count: number = RANDOM_TOPICS_COUNT): Topic[] => {
const shuffledTopics = [...topics].sort(() => 0.5 - Math.random())
return shuffledTopics.slice(0, count)
}