Player 2
Ahnaf An Nafee / December 01, 2022
react-native
react
expo
next.js
typescript
aws
java
spring-boot
postgresql
android
ios
🔴🔵 Player 2 - The Gamer's Companion

Overview
Player 2 is designed to be the foremost gamer networking app and website for players across PC, console, and mobile platforms. Whether a gamer needs teammates to complete an immediate activity, wants to make new friends to game with, or would like to join or start a gaming community, the app is designed to help gamers find the community where they play best, no matter who they are. Player 2 offers a comprehensive solution for a gamer’s social networking and team-building needs with our proprietary compatibility matchmaking algorithm. Our platform also includes features to make new friends to play games with and join or start a gaming community.
Responsibilities
- Create and Implement 6 new DevOps pipelines to deliver fast OTA application updates, backend build status, and Infrastructure as Code
 - Spearhead backend migration to Amazon Elastic Beanstalk for auto-scaling server instances, reducing application load and costs by 80%
 - Automate the build and deployment process with GitHub Actions and Maven and use Serverless Lambda functions for monitoring, eliminating 85% of manual work
 - Manage 100% of existing AWS Cloud environments, automation, monitoring metrics, disaster recovery/backups, and capacity planning
 - Develop a custom API layer to handle all CRUD transactions, and JWT token management and implemented interceptors to embed custom headers
 - Translate designs & wireframes into high-quality code and developed responsive, reusable, universal components following React workflows
 - Engineer Java service integration for STOMP WebSockets for in-app chat functionality
 - Chart and Integrate Third-Party services and OAuth services with 20+ RESTful endpoints to create a scalable, user-facing application and increase user engagement
 - Reduced performance bottlenecks in the custom chat service using Redux state management by 80%, making the user experience lag-free
 - Designed and integrated APIs that processed match data to power matchmaking recommendations for thousands of users
 - Collaborated in weekly meetings with stakeholders and the project team to ensure streamlined communication
 
Blog Links
Code Snippets
Auth Provider that auto refreshes auth tokens when they expire
const dispatch = useAppDispatch()
const { authState } = useAppSelector((state) => state.auth)
// States
const [loaded, setLoaded] = useState(false)
// for multiple requests
let isRefreshing = false
let failedQueue: any[] = []
const refreshTokenFunc: any = null
const processQueue = async (error: any, token = null) => {
  console.log('processQueue')
  failedQueue.forEach((prom) => {
    if (error) {
      prom.reject(error)
    } else {
      prom.resolve(token)
    }
  })
  failedQueue = []
}
const logout = async () => {
  console.log('Logout called!')
  dispatch(authActions.resetAuthState())
  dispatch(authThunks.signOut({}))
  dispatch(authActions.resetState())
  dispatch({ type: 'LOGOUT', payload: undefined })
}
const loadJWT = useCallback(async () => {
  const jwt = await getJWTTokens()
  jwt && dispatch(authActions.setAuthState(jwt))
  setLoaded(true)
}, [])
useEffect(() => {
  if (!loaded) {
    loadJWT()
  }
}, [loaded])
axios.interceptors.request.use(
  async (config) => {
    if (authState.authorizationToken && !config.headers!.Authorization) {
      /*
       * Does not add Bearer Token to auth URLs
       */
      if (!whitelistedPaths.some((path) => config.url?.includes(path))) {
        config.headers!.Authorization = 'Bearer ' + authState.authorizationToken
      }
    }
    return config
  },
  async (error) => {
    return Promise.reject(error)
  }
)
axios.interceptors.response.use(
  async (response) => {
    return response
  },
  async (error) => {
    const originalRequest = error.config
    if (error.response.status === 401 && !originalRequest._retry) {
      console.log('Failed Request', originalRequest)
      if (isRefreshing) {
        return new Promise((resolve, reject) => {
          if (typeof originalRequest.url === 'string' && originalRequest.url.includes('players/refresh')) {
            console.log('players/refresh fail')
            logout()
          }
          failedQueue.push({ resolve, reject })
        })
          .then((token) => {
            originalRequest.headers['Authorization'] = 'Bearer ' + token
            return axios(originalRequest)
          })
          .catch((err) => {
            return Promise.reject(err)
          })
      }
      originalRequest._retry = true
      isRefreshing = true
      const jwt = await getJWTTokens()
      if (jwt !== null) {
        return new Promise((resolve, reject) => {
          axios
            .post<UserTokens>(`${config.api.baseUrl}/players/refresh`, {
              tokens: { refreshToken: jwt?.refreshToken }
            })
            .then(async ({ data }) => {
              dispatch(authActions.setAuthState(data))
              originalRequest.headers['Authorization'] = 'Bearer ' + data.authorizationToken
              processQueue(null, data.authorizationToken as any)
              resolve(axios(originalRequest))
            })
            .catch((err) => {
              logout()
              processQueue(err, null)
              reject(err)
            })
            .finally(() => {
              isRefreshing = false
            })
        })
      }
      return Promise.reject(new Error('No token found'))
    }
    return Promise.reject(error)
  }
)
Screenshots
