Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat#1284✨: Added Loadmore button to People-> Created Bounties #35

Merged
merged 38 commits into from
Jan 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
f7611b1
0.1.1
Evanfeenstra Jan 23, 2024
07485db
fix docker path
Evanfeenstra Jan 23, 2024
410acd6
fix docker compose file
Evanfeenstra Jan 23, 2024
b0df733
nginx config
Evanfeenstra Jan 23, 2024
4664285
added Github actions
elraphty Jan 24, 2024
4e24eb5
fix(bounty-page): fixed bounty page go back to the home page
MirzaHanan Jan 24, 2024
32a6e5b
fix(bounty-page): fixed prettier format
MirzaHanan Jan 24, 2024
9e20485
bumped style components version
elraphty Jan 24, 2024
8655d32
Merge pull request #14 from stakwork/feat/git_workflows
elraphty Jan 24, 2024
28ce73c
Added Readme
VividhPandey003 Jan 24, 2024
8f7ab7a
Update README.md
VividhPandey003 Jan 24, 2024
2b47e1c
route frontend routes thru traefik
Evanfeenstra Jan 24, 2024
08ecfce
merge master
Evanfeenstra Jan 24, 2024
0e335df
Include labels in issue templates
ecurrencyhodler Jan 24, 2024
e395c7d
Just trigger our github actions workflows for testing
MirzaHanan Jan 24, 2024
40c9bea
Merge pull request #24 from stakwork/ecurrencyhodler-patch-1
ecurrencyhodler Jan 24, 2024
3772f41
fix(ui): keep `searchText` in local storage
YLeight Jan 23, 2024
e034d03
feat(people-search): add boundary hook for search
YLeight Jan 23, 2024
d197df1
refactor(people-list): rely on `usePeopleSearchHandler` hook
YLeight Jan 23, 2024
5016458
Merge branch 'master' into readme
VividhPandey003 Jan 25, 2024
faced56
updated backend api
VividhPandey003 Jan 25, 2024
60a1b81
implement the status filter on the org homepage
MahtabBukhari Jan 25, 2024
86947df
implement the organization website and github buttons on the org home…
aliraza556 Jan 25, 2024
31dcb7d
add the website and github icon
aliraza556 Jan 25, 2024
e5fd03f
Merge pull request #26 from aliraza556/Add-website-and-github-buttons
elraphty Jan 25, 2024
ddcd600
Merge pull request #22 from VividhPandey003/readme
elraphty Jan 25, 2024
7eb977f
Merge pull request #16 from MirzaHanan/bounty-page-go-back-home
elraphty Jan 25, 2024
e9a9a6f
Merge pull request #4 from YLeight/fix/1408-people-search-on-person-page
elraphty Jan 25, 2024
ecbf69e
Merge branch 'master' into org-page-status-filter
MahtabBukhari Jan 25, 2024
e683204
fix prettier issues
MahtabBukhari Jan 25, 2024
be2ef45
Merge pull request #25 from MahtabBukhari/org-page-status-filter
elraphty Jan 25, 2024
6a61c64
Merge pull request #6 from YLeight/fix/1437-keep-search-string-after-…
elraphty Jan 25, 2024
5f7f515
0.1.5
Evanfeenstra Jan 25, 2024
5fcc86e
notes
Evanfeenstra Jan 25, 2024
b9c6bb8
Merge branch 'stakwork:master' into master
VividhPandey003 Jan 26, 2024
d10076d
added loadmore to created bounties
VividhPandey003 Jan 26, 2024
fdb94c2
test and prettier
VividhPandey003 Jan 26, 2024
62aef34
prettier fix
VividhPandey003 Jan 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions deploy/staging/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
version: "2"

services:
reverse-proxy:
# The official v2 Traefik docker image
image: traefik:v2.2.1
# Enables the web UI and tells Traefik to listen to docker
# command: --configFile=/home/ec2-user/sphinx-deploy/traefik.yaml
ports:
# The HTTP port
- 80:80
# The Web UI (enabled by --api.insecure=true)
- 8080:8080
# entrypoints
- 443:443
- 8883:8883
volumes:
# So that Traefik can listen to the Docker events
- /var/run/docker.sock:/var/run/docker.sock
- /home/ec2-user/sphinx-deploy/traefik.yaml:/etc/traefik/traefik.yaml
- /home/ec2-user/letsencrypt:/letsencrypt
environment:
- AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
- AWS_REGION=$AWS_REGION
logging:
options:
max-size: 10m
ulimits:
nproc: 65535
nofile:
soft: 1000000
hard: 1000000

auth:
image: sphinx-auth
depends_on:
- reverse-proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.auth.rule=Host(`auth-test.sphinx.chat`)"
- "traefik.http.services.auth.loadbalancer.server.port=9090"
- "traefik.http.routers.auth.tls=true"
- "traefik.http.routers.auth.tls.certresolver=myresolver"
- "traefik.http.routers.auth.entrypoints=websecure"
restart: on-failure
environment:
- JWT_KEY=$JWT_KEY
- CLIENT_KEYS=$CLIENT_KEYS
- OAUTH_TIMEOUT=$OAUTH_TIMEOUT
- HOST=auth-test.sphinx.chat

tribes:
image: sphinx-tribes
depends_on:
- auth
- reverse-proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.tribes.rule=Host(`tribes-test.sphinx.chat`) || Host(`people-test.sphinx.chat`) || Host(`community-test.sphinx.chat`)"
- "traefik.http.routers.tribes.priority=1"
- "traefik.http.services.tribes.loadbalancer.server.port=5002"
- "traefik.http.routers.tribes.tls=true"
- "traefik.http.routers.tribes.tls.certresolver=myresolver"
- "traefik.http.routers.tribes.entrypoints=websecure"
restart: on-failure
environment:
- DATABASE_URL=$DATABASE_URL
- PODCAST_INDEX_SECRET=$PODCAST_INDEX_SECRET
- PODCAST_INDEX_KEY=$PODCAST_INDEX_KEY
- YOUTUBE_KEY=$YOUTUBE_KEY
- GITHUB_TOKEN=$GITHUB_TOKEN
- LN_SERVER_BASE_URL=https://people-test.sphinx.chat
- LN_JWT_KEY=$LN_JWT_KEY
- RELAY_URL=$RELAY_URL
- RELAY_AUTH_KEY=$RELAY_AUTH_KEY
- RELAY_URL_BACKUP=$RELAY_URL_BACKUP
- RELAY_AUTH_KEY_BACKUP=$RELAY_AUTH_KEY_BACKUP
- HOST=people-test.sphinx.chat
- REDIS_URL=$REDIS_URL
- ADMIN_PUBKEYS=$ADMINS
- ADMINS=$ADMINS
- AWS_SECRET_ACCESS=$AWS_SECRET_ACCESS_KEY
- AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
- AWS_REGION=us-east-1
- S3_BUCKET_NAME=sphinx-tribes
- S3_FOLDER_NAME=metrics
- S3_URL=https://sphinx-tribes.s3.amazonaws.com
ulimits:
nproc: 65535
nofile:
soft: 1000000
hard: 1000000

tribes-frontend:
image: sphinxlightning/sphinx-tribes-frontend:latest
depends_on:
- tribes
- reverse-proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.tribes-frontend.rule=(Host(`tribes-test.sphinx.chat`) || Host(`people-test.sphinx.chat`) || Host(`community-test.sphinx.chat`)) && (Path(`/`) || PathPrefix(`/static`) || Path(`/manifest.json`) || Path(`/favicon.ico`) || Path(`/logo192.png`) || PathPrefix(`/t/`) || Path(`/t`) || PathPrefix(`/p/`) || Path(`/p`) || Path(`/tickets`) || Path(`/bounties`) || Path(`/bounty`) || PathPrefix(`/bounty/`) || Path(`/leaderboard`) || PathPrefix(`/org/`) || Path(`/admin`))"
- "traefik.http.routers.tribes-frontend.priority=2"
- "traefik.http.services.tribes-frontend.loadbalancer.server.port=80"
- "traefik.http.routers.tribes-frontend.tls=true"
- "traefik.http.routers.tribes-frontend.tls.certresolver=myresolver"
- "traefik.http.routers.tribes-frontend.entrypoints=websecure"
restart: on-failure

mqtt:
image: sphinx-mqtt
depends_on:
- auth
- reverse-proxy
labels:
- "traefik.enable=true"
- "traefik.tcp.routers.mqtt.rule=HostSNI(`tribes-test.sphinx.chat`)"
- "traefik.tcp.services.mqtt.loadbalancer.server.port=1883"
- "traefik.tcp.routers.mqtt.tls=true"
- "traefik.tcp.routers.mqtt.tls.certresolver=myresolver"
- "traefik.tcp.routers.mqtt.entrypoints=mqttsecure"
- "traefik.tcp.routers.mqtt.service=mqtt"
restart: on-failure
ulimits:
nproc: 65535
nofile:
soft: 1000000
hard: 1000000
43 changes: 43 additions & 0 deletions src/pages/people/tabs/CreatedBountyLoadMore.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react';
import { render, fireEvent, waitFor } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import mockBounties from '../../../bounties/__mock__/mockBounties.data';
import '@testing-library/jest-dom/extend-expect';
import { Wanted } from './Wanted';

// eslint-disable-next-line @typescript-eslint/typedef
const createdMockBounties = Array.from({ length: 15 }, (_, index) => ({
...(mockBounties[0] || {}),
bounty: {
...(mockBounties[0]?.bounty || {}),
id: mockBounties[0]?.bounty?.id + index + 1
}
}));

console.log(createdMockBounties);

jest.mock('../../../bounties/__mock__/mockBounties.data', () => ({
createdMockBounties
}));

describe('Wanted component', () => {
it('displays "Load More" button when scrolling down', async () => {
const { getByText } = render(
<MemoryRouter>
<Wanted />
</MemoryRouter>
);

fireEvent.scroll(window, { target: { scrollY: 1000 } });

// Wait for the component to re-render (assuming there's an API call)
await waitFor(() => {
// Check if the "Load More" button is displayed
if (createdMockBounties.length > 20) {
expect(getByText('Load More')).toBeInTheDocument();
} else {
console.warn('Not enough bounties for "Load More" button.');
}
});
});
});
92 changes: 69 additions & 23 deletions src/pages/people/tabs/Wanted.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@ import PageLoadSpinner from 'people/utils/PageLoadSpinner';
import React, { useEffect, useState } from 'react';
import { Route, Switch, useHistory, useRouteMatch, useParams } from 'react-router-dom';
import { useStores } from 'store';
import { paginationQueryLimit } from 'store/main';
import styled from 'styled-components';
import { LoadMoreContainer } from '../../../people/widgetViews/WidgetSwitchViewer';
import { colors } from '../../../config/colors';
const config = widgetConfigs.wanted;

type BountyType = any;
const Container = styled.div`
display: flex;
flex-flow: row wrap;
Expand Down Expand Up @@ -45,15 +48,44 @@ export const Wanted = observer(() => {
const { path, url } = useRouteMatch();
const history = useHistory();
const { personPubkey } = useParams<{ personPubkey: string }>();
const [displayedBounties, setDisplayedBounties] = useState<BountyType[]>([]);
const [loading, setIsLoading] = useState<boolean>(false);
const [page, setPage] = useState(1);
const [hasMoreBounties, setHasMoreBounties] = useState(true);

// Function to fetch user tickets with pagination
async function getUserTickets() {
setIsLoading(true);
await main.getPersonCreatedBounties({}, personPubkey);
await main.getPersonAssignedBounties({ sortBy: 'paid' }, personPubkey);
// Fetch bounties for the specified page and limit
const response = await main.getPersonCreatedBounties(
{ page: page, limit: paginationQueryLimit },
personPubkey
);
// Check if the response has fewer bounties than the limit, indicating no more bounties to load
if (response.length < paginationQueryLimit) {
setHasMoreBounties(false);
}
// Update the displayed bounties by appending the new bounties
setDisplayedBounties((prevBounties: BountyType[]) => [...prevBounties, ...response]);
setIsLoading(false);
}

const nextBounties = async () => {
const nextPage = page + 1;
setPage(nextPage);
// Fetch bounties for the next page
const response = await main.getPersonCreatedBounties(
{ page: nextPage, limit: paginationQueryLimit },
personPubkey
);
// Check if the response has fewer bounties than the limit, indicating no more bounties to load
if (response.length < paginationQueryLimit) {
setHasMoreBounties(false);
}
// Update the displayed bounties by appending the new bounties
setDisplayedBounties((prevBounties: BountyType[]) => [...prevBounties, ...response]);
};

useEffect(() => {
getUserTickets();
}, [main]);
Expand Down Expand Up @@ -106,26 +138,40 @@ export const Wanted = observer(() => {
>
{canEdit && <PostBounty widget="wanted" />}
</div>
{main.createdBounties.map((w: any, i: any) => {
if (w.body.owner_id === person?.owner_pubkey) {
return (
<Panel
href={`${url}/${w.body.id}/${i}`}
key={w.body.id}
isMobile={false}
onClick={(e: any) => {
e.preventDefault();
ui.setBountyPerson(person?.id);
history.push({
pathname: `${url}/${w.body.id}/${i}`
});
}}
>
<WantedView {...w.body} person={person} />
</Panel>
);
}
})}
{displayedBounties
.filter((w: BountyType) => w.body.owner_id === person?.owner_pubkey)
.map((w: BountyType, i: any) => (
<Panel
href={`${url}/${w.body.id}/${i}`}
key={w.body.id}
isMobile={false}
onClick={(e: any) => {
e.preventDefault();
ui.setBountyPerson(person?.id);
history.push({
pathname: `${url}/${w.body.id}/${i}`
});
}}
>
<WantedView {...w.body} person={person} />
</Panel>
))}
{hasMoreBounties && !loading && (
<LoadMoreContainer
color={colors['light']}
style={{
width: '100%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
margin: '20px 0'
}}
>
<div className="LoadMoreButton" onClick={nextBounties}>
Load More
</div>
</LoadMoreContainer>
)}
</Container>
);
});
15 changes: 10 additions & 5 deletions src/store/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { uiStore } from './ui';
import { getUserAvatarPlaceholder } from './lib';

export const queryLimit = 10;
export const paginationQueryLimit = 20;
export const peopleQueryLimit = 500;

function makeTorSaveURL(host: string, key: string) {
Expand Down Expand Up @@ -920,10 +921,14 @@ export class MainStore {
async getPersonAssignedBounties(queryParams?: any, pubkey?: string): Promise<PersonBounty[]> {
queryParams = { ...queryParams, search: uiStore.searchText };

const query = this.appendQueryParams(`people/wanteds/assigned/${pubkey}`, queryLimit, {
sortBy: 'paid',
...queryParams
});
const query = this.appendQueryParams(
`people/wanteds/assigned/${pubkey}`,
paginationQueryLimit,
{
sortBy: 'paid',
...queryParams
}
);

try {
const ps2 = await api.get(query);
Expand Down Expand Up @@ -967,7 +972,7 @@ export class MainStore {
async getPersonCreatedBounties(queryParams?: any, pubkey?: string): Promise<PersonBounty[]> {
queryParams = { ...queryParams, search: uiStore.searchText };

const query = this.appendQueryParams(`people/wanteds/created/${pubkey}`, 20, {
const query = this.appendQueryParams(`people/wanteds/created/${pubkey}`, paginationQueryLimit, {
...queryParams,
sortBy: 'paid'
});
Expand Down
Loading