diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 370eeac58..f9de74c71 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,6 +6,7 @@ updates: reviewers: - 'oap75' - 'olehmell' + - "iv1310" schedule: interval: 'weekly' commit-message: diff --git a/README.md b/README.md index 5013ea38b..73efdac9b 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,61 @@ PolkaVerse is a niche social site built on the Subsocial network. It focuses on Visit Subsocial's [website](https://subsocial.network) to learn more about the project. +## Guide to build and deploy polkaverse + +### Build the docker images +1. Prepare the [dockerfile](./docker/Dockerfile) and adjust the config if needed. +2. Build the image in local with this command and please ensure to add build argument. +```bash +$ docker build --build-arg GH_GA_ID=valueREDACTED --build-arg GH_APP_KIND=valueREDACTED --build-arg GH_HCAPTCHA_SITE_KEY=valueREDACTED --build-arg GH_AMP_ID=valueREDACTED --build-arg GH_OFFCHAIN_SIGNER_URL=valueREDACTED --build-arg GH_CONNECTION_KIND=valueREDACTED --build-arg GH_SELLER_CLIENT_ID=valueREDACTED --build-arg GH_SERVER_MNEMONIC==valueREDACTED --build-arg GH_SELLER_TOKEN_SIGNER=valueREDACTED --build-arg GH_NEXT_PUBLIC_DATAHUB_QUERY_URL=valueREDACTED --build-arg GH_NEXT_PUBLIC_DATAHUB_SUBSCRIPTION_URL=valueREDACTED --build-arg GH_DATAHUB_QUEUE_URL=valueREDACTED --build-arg GH_DATAHUB_QUEUE_TOKEN=valueREDACTED -t polkaverse-docker-image:latest . +``` +Notes: +Please execute the build process with theses build arguments, you need to specify the value. +* GH_GA_ID=valueREDACTED +* GH_APP_KIND=valueREDACTED +* GH_HCAPTCHA_SITE_KEY=valueREDACTED +* GH_AMP_ID=valueREDACTED +* GH_OFFCHAIN_SIGNER_URL=valueREDACTED +* GH_CONNECTION_KIND=valueREDACTED +* GH_SELLER_CLIENT_ID=valueREDACTED +* GH_SELLER_TOKEN_SIGNER=valueREDACTED +* GH_SERVER_MNEMONIC=valueREDACTED +* GH_NEXT_PUBLIC_DATAHUB_QUERY_URL=valueREDACTED +* GH_NEXT_PUBLIC_DATAHUB_SUBSCRIPTION_URL=valueREDACTED +* GH_DATAHUB_QUEUE_URL=valueREDACTED +* GH_DATAHUB_QUEUE_TOKEN=valueREDACTED +3. Then check the docker images that has been builded. +```bash +$ docker images | grep "polkaverse" +``` + +### Run the container with docker-compose +1. To run the docker images with docker-compose, please prepare the docker-compose.yaml config file at first. +```yaml +# docker-compose.yml +version: "3" +services: + web-ui: + image: polkaverse-docker-image:latest + ports: + - "3003:3003" # Application port + container_name: polkaverse-web-app + restart: on-failure +``` +```bash +$ docker-compose -f docker-compose.yaml up -d +``` +2. Check the running container with this command. +```bash +$ docker-compose ps +$ docker-compose logs +``` +3. Test to connect to the application. +```bash +$ curl -I http://localhost:3003 +``` + + ## Run locally Clone this repo: diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 9bccc0272..1efa720e6 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -9,10 +9,3 @@ services: container_name: subsocial-web-ui restart: on-failure network_mode: "host" - - nginx: - build: ./nginx - container_name: subsocial-proxy - image: dappforce/subsocial-proxy:latest - restart: on-failure - network_mode: "host" diff --git a/docker/nginx/Dockerfile b/docker/nginx/Dockerfile deleted file mode 100644 index 954ed60ea..000000000 --- a/docker/nginx/Dockerfile +++ /dev/null @@ -1,9 +0,0 @@ -FROM nginx:alpine - -RUN rm /etc/nginx/conf.d/* - -COPY ./default.conf /etc/nginx/conf.d/ - -EXPOSE 80 - -CMD [ "nginx", "-g", "daemon off;" ] \ No newline at end of file diff --git a/docker/nginx/default.conf b/docker/nginx/default.conf deleted file mode 100644 index 0eaee85be..000000000 --- a/docker/nginx/default.conf +++ /dev/null @@ -1,16 +0,0 @@ -server { - listen 80 default_server; - - server_name _; - - server_tokens off; - - location / { - proxy_pass http://localhost:3003; - } - - location /bc/ { - rewrite ^/bc(.*) /$1 break; - proxy_pass http://localhost:3002; - } -} diff --git a/src/components/activity/MyNotifications.tsx b/src/components/activity/MyNotifications.tsx index e62c96124..92b45c589 100644 --- a/src/components/activity/MyNotifications.tsx +++ b/src/components/activity/MyNotifications.tsx @@ -10,13 +10,13 @@ const NOTIFICATION_TITLE = 'My notifications' export const MyNotifications = () => { const myAddress = useMyAddress() - const isInitializedProxy = useMyAccount(state => state.isInitializedProxy) + const isInitialized = useMyAccount(state => state.isInitialized) if (!myAddress) return return ( - {!isInitializedProxy ? ( + {!isInitialized ? ( ) : ( diff --git a/src/components/activity/NotifCounter.tsx b/src/components/activity/NotifCounter.tsx index d428b0101..af9227f7c 100644 --- a/src/components/activity/NotifCounter.tsx +++ b/src/components/activity/NotifCounter.tsx @@ -34,7 +34,7 @@ function InnerNotifCounterProvider(props: React.PropsWithChildren<{}>) { storageKeyType: 'user', }) const myAddress = useMyAddress() - const isInitializedProxy = useMyAccount(state => state.isInitializedProxy) + const isInitialized = useMyAccount(state => state.isInitialized) const [unreadCount, setUnreadCount] = useState(0) const [previousLastRead, setPreviousLastRead] = useState(null) @@ -47,7 +47,7 @@ function InnerNotifCounterProvider(props: React.PropsWithChildren<{}>) { } useEffect(() => { - if (!isInitializedProxy || !myAddress) return + if (!isInitialized || !myAddress) return ;(async () => { const unreadCount = await getNotificationsCount({ address: myAddress, @@ -55,7 +55,7 @@ function InnerNotifCounterProvider(props: React.PropsWithChildren<{}>) { }) setUnreadCount(unreadCount) })() - }, [myAddress, isInitializedProxy]) + }, [myAddress, isInitialized]) return ( ( export const NotificationsBell = ({ unreadCount }: NotificationsProps) => { const myAddress = useMyAddress() - const isInitializedProxy = useMyAccount(state => state.isInitializedProxy) + const isInitialized = useMyAccount(state => state.isInitialized) const { getLastReadNotif } = useNotifCounterContext() if (!enableNotifications) return null - if (!unreadCount || unreadCount <= 0 || !isInitializedProxy) - return + if (!unreadCount || unreadCount <= 0 || !isInitialized) return const showWithoutCount = !getLastReadNotif(myAddress) diff --git a/src/components/auth/MyAccountsContext.tsx b/src/components/auth/MyAccountsContext.tsx index e41e01622..617032223 100644 --- a/src/components/auth/MyAccountsContext.tsx +++ b/src/components/auth/MyAccountsContext.tsx @@ -29,13 +29,14 @@ import { fetchChainsInfo } from 'src/rtk/features/chainsInfo/chainsInfoSlice' import { fetchProfileSpace } from 'src/rtk/features/profiles/profilesSlice' import { fetchEntityOfSpaceIdsByFollower } from 'src/rtk/features/spaceIds/followedSpaceIdsSlice' import { useMyAccount } from 'src/stores/my-account' -import { AnyAccountId, EmailAccount } from 'src/types' +import { AnyAccountId, DataSourceTypes, EmailAccount } from 'src/types' import useSubsocialEffect from '../api/useSubsocialEffect' import { useAccountSelector } from '../profile-selector/AccountSelector' import { useIsMobileWidthOrDevice } from '../responsive' import { reloadSpaceIdsFollowedByAccount } from '../spaces/helpers/reloadSpaceIdsFollowedByAccount' import { equalAddresses } from '../substrate' import { getSignerToken, isProxyAdded } from '../utils/OffchainSigner/ExternalStorage' +import { getSubsocialApi } from '../utils/SubsocialConnect' import { desktopWalletConnect, mobileWalletConection } from './utils' // // Types @@ -101,6 +102,7 @@ export function MyAccountsProvider(props: React.PropsWithChildren<{}>) { const reloadAccountIdsByFollower = useCreateReloadAccountIdsByFollower() const reloadSpaceIdsRelatedToAccount = useCreateReloadSpaceIdsRelatedToAccount() const address = useMyAddress() + const isInitialized = useMyAccount(state => state.isInitialized) const { getAllEmailAccounts } = useEmailAccount() const [, recheck] = useReducer(x => (x + 1) % 16384, 0) const isMobile = useIsMobileWidthOrDevice() @@ -132,7 +134,7 @@ export function MyAccountsProvider(props: React.PropsWithChildren<{}>) { }, [status]) useSubsocialEffect( - ({ substrate, subsocial }) => { + ({ substrate }) => { if (!address) return let unsubAccountInfo: UnsubscribeFn @@ -141,13 +143,10 @@ export function MyAccountsProvider(props: React.PropsWithChildren<{}>) { const readyApi = await substrate.api Promise.all([ - reloadSpaceIdsFollowedByAccount({ substrate, dispatch: dispatch, account: address }), + reloadSpaceIdsFollowedByAccount({ substrate, dispatch, account: address }), reloadAccountIdsByFollower(address), reloadSpaceIdsRelatedToAccount(address), - dispatch(fetchProfileSpace({ id: address, api: subsocial })), - dispatch(fetchEntityOfSpaceIdsByFollower({ id: address, reload: true, api: subsocial })), dispatch(fetchChainsInfo({})), - dispatch(fetchAddressLikeCounts({ address, postIds: null })), ]) unsubAccountInfo = await readyApi.query.system.account( @@ -166,6 +165,20 @@ export function MyAccountsProvider(props: React.PropsWithChildren<{}>) { }, [address], ) + useEffect(() => { + if (!isInitialized || !address) return + dispatch( + fetchEntityOfSpaceIdsByFollower({ + id: address, + dataSource: DataSourceTypes.SQUID, + api: getSubsocialApi(), + }), + ) + dispatch( + fetchProfileSpace({ id: address, api: getSubsocialApi(), dataSource: DataSourceTypes.SQUID }), + ) + dispatch(fetchAddressLikeCounts({ address, postIds: null })) + }, [address, isInitialized]) const state = useMemo( () => ({ accounts, status, emailAccounts }), diff --git a/src/components/main/HomePage.tsx b/src/components/main/HomePage.tsx index 6e04e7fd3..ccf0ce860 100644 --- a/src/components/main/HomePage.tsx +++ b/src/components/main/HomePage.tsx @@ -7,7 +7,10 @@ import { useCallback, useEffect, useState } from 'react' import config from 'src/config' import { useSendEvent } from 'src/providers/AnalyticContext' import { getInitialPropsWithRedux } from 'src/rtk/app' +import { useAppSelector } from 'src/rtk/app/store' import { useFetchTotalStake } from 'src/rtk/features/creators/totalStakeHooks' +import { selectSpaceIdsByFollower } from 'src/rtk/features/spaceIds/followedSpaceIdsSlice' +import { useMyAccount } from 'src/stores/my-account' import { PostKind } from 'src/types/graphql-global-types' import { getAmountRange } from 'src/utils/analytics' import { useIsSignedIn, useMyAddress } from '../auth/MyAccountsContext' @@ -147,9 +150,20 @@ const TabsHomePage = ({ setFiltersInUrl(router, key, filterType, { ref: refId }) } + const isInitialized = useMyAccount(state => state.isInitialized) + const followedIds = useAppSelector(state => { + return selectSpaceIdsByFollower(state, myAddress) + }) + + const isLoadingFollowedIds = followedIds === undefined useEffect(() => { - onChangeKey(tab) - }, [isSignedIn]) + if (!isInitialized || !isSignedIn || isLoadingFollowedIds) return + if (followedIds.length === 0) { + setFiltersInUrl(router, 'posts', { type: 'hot' }, { ref: refId }) + } else { + onChangeKey(tab) + } + }, [followedIds, isInitialized]) const handleScroll = () => { const currentScrollPos = window.pageYOffset diff --git a/src/components/posts/editor/FullEditor.tsx b/src/components/posts/editor/FullEditor.tsx index c55a5b8e7..42ce7529e 100644 --- a/src/components/posts/editor/FullEditor.tsx +++ b/src/components/posts/editor/FullEditor.tsx @@ -195,7 +195,10 @@ const FullEditor = ({ {!totalStake?.hasStakedEnough && ( diff --git a/src/components/posts/editor/ModalEditor.tsx b/src/components/posts/editor/ModalEditor.tsx index c72e05ad8..c9343b36a 100644 --- a/src/components/posts/editor/ModalEditor.tsx +++ b/src/components/posts/editor/ModalEditor.tsx @@ -105,7 +105,7 @@ export const PostEditorModalBody = ({ const [spaceId, setSpaceId] = useState(defaultSpace) const { loading } = useFetchSpaces({ ids: spaceIds, dataSource: DataSourceTypes.SQUID }) - const { saveContent } = useAutoSaveFromForm({ entity: 'post' }) + const { savedData, saveContent } = useAutoSaveFromForm({ entity: 'post' }) useEffect(() => { setSpaceId(defaultSpace) @@ -201,7 +201,7 @@ export const PostEditorModalBody = ({ ) return ( -
saveDraft()}> + saveDraft()}> {/* value and onChange are provided by Form.Item */} diff --git a/src/components/posts/editor/index.module.sass b/src/components/posts/editor/index.module.sass index 0953206d3..feb1dbef5 100644 --- a/src/components/posts/editor/index.module.sass +++ b/src/components/posts/editor/index.module.sass @@ -35,6 +35,9 @@ \:global .ant-tabs-nav margin: 0 +.EditorBodyContentContainer + width: $max_width_content + .AdvancedBody width: 325px diff --git a/src/components/posts/pinned-post.ts b/src/components/posts/pinned-post.ts index 230686cd9..ddafadef1 100644 --- a/src/components/posts/pinned-post.ts +++ b/src/components/posts/pinned-post.ts @@ -1,7 +1,7 @@ import { getLastestPostIdsInSpace } from 'src/graphql/apis' import { GqlClient } from 'src/graphql/ApolloProvider' -const PINNED_POST_IDS: string[] = ['91982'] +const PINNED_POST_IDS: string[] = ['100229'] export const PINNED_POST_ID = PINNED_POST_IDS[Math.floor(Math.random() * PINNED_POST_IDS.length)] const COMMUNITY_SPACE_ID = '1244' diff --git a/src/stores/my-account.ts b/src/stores/my-account.ts index d50f218b1..aa073fef9 100644 --- a/src/stores/my-account.ts +++ b/src/stores/my-account.ts @@ -16,7 +16,14 @@ import { useAnalytics } from './analytics' import { create } from './utils' type State = { + /** + * `isInitialized` is `true` when the addresses (address & parentProxyAddress) are all set + * but there is still a case where the proxy is invalid and user will be logged out after that + */ isInitialized?: boolean + /** + * `isInitializedProxy` is `true` when the initialization process is all done, including checking the proxy + */ isInitializedProxy?: boolean preferredWallet: any | null @@ -148,13 +155,14 @@ export const useMyAccount = create()((set, get) => ({ accountStorage.remove() accountAddressStorage.remove() set({ address: null }) + } else { + accountAddressStorage.set(address) } } - set({ isInitialized: true }) + set({ isInitialized: true, parentProxyAddress: parentProxyAddress || undefined }) if (parentProxyAddress) { - set({ parentProxyAddress }) try { const proxies = await getProxies(parentProxyAddress) const currentProxy = proxies.find(({ address }) => address === get().address)