Skip to content

Commit

Permalink
Merge pull request #945 from MirzaHanan/feature/bounty-elapsed-timer
Browse files Browse the repository at this point in the history
Implement Real-Time Elapsed Time Tracker for Assigned Bounties in Modal
  • Loading branch information
humansinstitute authored Jan 18, 2025
2 parents 8a35325 + 64bd281 commit edde8b7
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 1 deletion.
95 changes: 95 additions & 0 deletions src/components/common/ElapsedTimer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import React, { useState, useEffect } from 'react';
import { observer } from 'mobx-react-lite';
import { bountyReviewStore } from 'store/bountyReviewStore';
import styled from 'styled-components';
import { formatElapsedTime } from '../../helpers/timeFormatting';

interface ElapsedTimerProps {
bountyId: string;
}

const TimerContainer = styled.div`
display: flex;
flex-direction: column;
gap: 3px;
`;

const Label = styled.span`
font-size: 16px;
font-weight: 700;
color: #8f8f8f;
display: flex;
justify-content: center;
align-items: center;
font-family: 'Barlow';
`;

const Time = styled.span`
font-size: 20px;
font-weight: 600;
color: #000000;
text-align: center;
`;

const ElapsedTimerBase: React.FC<ElapsedTimerProps> = ({ bountyId }: ElapsedTimerProps) => {
const [elapsedTime, setElapsedTime] = useState<string>('00h 00m');
const [firstAssignedAt, setFirstAssignedAt] = useState<string | null>(null);
const [error, setError] = useState<boolean>(false);

useEffect(() => {
const fetchTiming = async () => {
if (bountyId === '0') {
setError(false);
return;
}

try {
await bountyReviewStore.getTiming(bountyId);
const timing = bountyReviewStore.timings[bountyId];

if (timing?.firstAssignedAt) {
setFirstAssignedAt(timing.firstAssignedAt);
setElapsedTime(formatElapsedTime(timing.firstAssignedAt));
setError(false);
} else {
setFirstAssignedAt(null);
setElapsedTime('00h 00m');
setError(false);
}
} catch (error) {
console.error('Error fetching timing:', error);
setError(true);
setFirstAssignedAt(null);
setElapsedTime('00h 00m');
}
};

fetchTiming();
}, [bountyId]);

useEffect(() => {
if (!firstAssignedAt) return;

const updateTimer = () => {
setElapsedTime(formatElapsedTime(firstAssignedAt));
};

updateTimer();
const interval = setInterval(updateTimer, 60000);

return () => clearInterval(interval);
}, [firstAssignedAt]);

if (error) {
return null;
}

return (
<TimerContainer>
<Label>Elapsed</Label>
<Time>{elapsedTime}</Time>
</TimerContainer>
);
};

export const ElapsedTimer = observer(ElapsedTimerBase);
11 changes: 11 additions & 0 deletions src/helpers/timeFormatting.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export const formatElapsedTime = (firstAssignedAt: string): string => {
const startTime = new Date(firstAssignedAt);
const currentTime = new Date();

const elapsed = currentTime.getTime() - startTime.getTime();

const hours = Math.floor(elapsed / (1000 * 60 * 60));
const minutes = Math.floor((elapsed % (1000 * 60 * 60)) / (1000 * 60));

return `${hours.toString().padStart(2, '0')}h ${minutes.toString().padStart(2, '0')}m`;
};
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import InvitePeopleSearch from '../../../../components/form/inputs/widgets/Peopl
import { CodingBountiesProps } from '../../../interfaces';
import LoomViewerRecorder from '../../../utils/LoomViewerRecorder';
import { paidString, unpaidString } from '../constants';
import { ElapsedTimer } from '../../../../components/common/ElapsedTimer';
import Invoice from './Invoice';
import {
AssigneeProfile,
Expand Down Expand Up @@ -49,7 +50,8 @@ import {
Section,
Title,
Description,
Status
Status,
ElapsedTimerContainer
} from './style';
import { getTwitterLink } from './lib';
import CodingMobile from './CodingMobile';
Expand Down Expand Up @@ -1019,6 +1021,16 @@ function MobileView(props: CodingBountiesProps) {
</div>
)}
</UnassignedPersonProfile>
{userAssigned && (
<>
<DividerContainer>
<Divider />
</DividerContainer>
<ElapsedTimerContainer>
<ElapsedTimer bountyId={bountyID} />
</ElapsedTimerContainer>
</>
)}
{payment_failed && (
<ErrorWrapper>
<Divider />
Expand Down
7 changes: 7 additions & 0 deletions src/people/widgetViews/summaries/wantedSummaries/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -798,3 +798,10 @@ export const DeliverablesContainer = styled.div`
}
}
`;

export const ElapsedTimerContainer = styled.div`
width: 100%;
display: flex;
justify-content: center;
align-items: center;
`;

0 comments on commit edde8b7

Please sign in to comment.