Order Subscription

김종민·2022년 10월 11일
0

Nuber-Client

목록 보기
21/21

Driver Dashboard의 마지막 부분.
Order가 들어오고
Owner가 status를 cooked를 했다면, Driver에게
Order가 push로 가고,
Driver가 status를 PickedUp, Deliverd라고
바꿔줄 수 있다.
또한, Client의 위치가 구글맵에 찍히게 해 본다.
챌린지로~


1. src/pages/driver/dashboard.tsx

import React, { useEffect, useState } from 'react'
import GoogleMapReact from 'google-map-react'
import { gql, useMutation, useSubscription } from '@apollo/client'
import { FULL_ORDER_FRAGMENT } from '../../fragments'
import {
  CookedOrdersSubscription,
  TakeOrderMutation,
  TakeOrderMutationVariables,
} from '../../graphql/__generated__'
import { Link, useNavigate } from 'react-router-dom'

const COOCKED_ORDERS_SUBSCRIPTION = gql`
  subscription coockedOrders {
    cookedOrders {
      ...FullOrderParts
    }
  }
  ${FULL_ORDER_FRAGMENT}
`
///coockedOrders subscription을 server로부터 불러온다.
///아래의 server쪽 code 참조할 것.

const TAKE_ORDER_MUTATION = gql`
  mutation takeOrder($input: TakeOrderInput!) {
    takeOrder(input: $input) {
      ok
      error
    }
  }
`
///takeOrder mutation
///driver가 takeOrder할 예정.

interface ICoords {
  lat: number
  lng: number
}

interface IDriverProps {
  lat: number
  lng: number
  $hover?: any
}

const Driver: React.FC<IDriverProps> = () => <div className="text-lg">🚚</div>

export const Dashboard = () => {
  const [driverCoords, setDriverCoords] = useState<ICoords>({ lat: 0, lng: 0 })
  const [map, setMap] = useState<google.maps.Map>()
  const [maps, setMaps] = useState<any>()
  const onSuccess = ({
    coords: { latitude, longitude },
  }: GeolocationPosition) => {
    setDriverCoords({ lat: latitude, lng: longitude })
  }
  const onError = (error: GeolocationPositionError) => {
    console.log(error)
  }
  useEffect(() => {
    navigator.geolocation.watchPosition(onSuccess, onError, {
      enableHighAccuracy: true,
    })
  }, [])
  useEffect(() => {
    if (map && maps) {
      map.panTo(new google.maps.LatLng(driverCoords.lat, driverCoords.lng))
      const geocoder = new google.maps.Geocoder()
      geocoder.geocode(
        {
          location: new google.maps.LatLng(driverCoords.lat, driverCoords.lng),
        },
        (result, status) => {
          console.log(result, status)
        }
      )
    }
  }, [driverCoords.lat, driverCoords.lng])

  const onApiLoaded = ({ map, maps }: { map: any; maps: any }) => {
    map.panTo(new google.maps.LatLng(driverCoords.lat, driverCoords.lng))
    setMap(map)
    setMaps(maps)
  }
  
  
  const makeRoute = () => {
    if (map) {
      const directionsService = new google.maps.DirectionsService()
      const directionsRenderer = new google.maps.DirectionsRenderer()
      directionsRenderer.setMap(map)
      directionsService.route(
        {
          origin: {
            location: new google.maps.LatLng(
              driverCoords.lat,
              driverCoords.lng
            ),
          },
          destination: {
            location: new google.maps.LatLng(
              driverCoords.lat + 0.01,
              driverCoords.lng + 0.01
            ),
          },
          travelMode: google.maps.TravelMode.TRANSIT,
        },
        (result, status) => {
          directionsRenderer.setDirections(result)
        }
      )
    }
  }
  
  const { data: coockedOrdersData } = useSubscription<CookedOrdersSubscription>(
    COOCKED_ORDERS_SUBSCRIPTION
  )
  
  ///subscription Data가 있으면, 
  ///makeRoute 함수를 실행해서, 구글지도에 길을 그려줌.
  useEffect(() => {
    if (coockedOrdersData?.cookedOrders.id) {
      makeRoute()
    }
  }, [coockedOrdersData])

///takeOrder가 실행되면, order page로 이동시켜줌.
  const navigate = useNavigate()
  const onCompleted = (data: TakeOrderMutation) => {
    if (data.takeOrder.ok) {
      navigate(`/orders/${coockedOrdersData?.cookedOrders.id}`)
    }
  }

///takeOrder mutation을 실행했을때,
  const [takeOrderMutation] = useMutation<
    TakeOrderMutation,
    TakeOrderMutationVariables
  >(TAKE_ORDER_MUTATION, { onCompleted })
  
///아래부분의 return()의 accept Challenge버튼을 눌렸을떄,
///아래 accept Chanllenge에서 ///coockedOrdersData?.cookedOrders.id를 받아와서
///orderId로 받아서, takeOrderMutation의 
///variables에 넣어서 takeOrderMutaiton을 실행시켜줌.
  const triggerMutation = (orderId: number) => {
    takeOrderMutation({
      variables: {
        input: {
          id: orderId,
        },
      },
    })
  }
  return (
    <div>
      <div style={{ height: '70vh', width: '100%' }}>
        <GoogleMapReact
          yesIWantToUseGoogleMapApiInternals
          onGoogleApiLoaded={onApiLoaded}
          bootstrapURLKeys={{ key: 'AIzaSyCQBuKy-bkomVZm4BTMYc30cCDvtCpateI' }}
          defaultCenter={{
            lat: 35.166535,
            lng: 126.9779692,
          }}
          defaultZoom={15}
        >
          <Driver lat={driverCoords.lat} lng={driverCoords.lng} />
        </GoogleMapReact>
      </div>

      <div className="max-w-screen-sm mx-auto bg-white relative -top-10 shadow-lg py-8 px-5">
        {coockedOrdersData?.cookedOrders.restaurant ? (
          <>
            <h1 className="text-center text-3xl font-medium">
              New Coocked Order
            </h1>
            <h4 className="text-center text-2xl font-medium">
              Pick it up soon! @{' '}
              {coockedOrdersData?.cookedOrders?.restaurant?.name}
            </h4>
            <button
              onClick={() =>
                triggerMutation(coockedOrdersData?.cookedOrders.id)
              }
              className="btn w-full text-center mt-5 block"
            >
              Accept Challenge
            </button>
            ///Accept Challenge버튼을 누르면, takeOrder
            ///mutation이 실행되게 함.
          </>
        ) : (
          <h1 className="text-center text-3xl font-medium">No Orders Yet</h1>
        )}
      </div>
    </div>
  )
}

!!!cookedOrdes Subscription(orders.resolvers.ts)

 @Subscription(() => Order)
  @Role(['Delivery'])
  cookedOrders() {
    return this.pubSub.asyncIterator(NEW_COOKED_ORDER);
  }

!!!editOrder(orders.service.ts)

  async editOrder(
    user: User,
    { id: orderId, status }: EditOrderInput,
  ): Promise<EditOrderOutput> {
    try {
      const order = await this.orders.findOne({
        where: {
          id: orderId,
        },
      });
      if (!order) {
        return {
          ok: false,
          error: 'Order not found',
        };
      }
      if (!this.canSeeOrder(user, order)) {
        return {
          ok: false,
          error: 'Can not see this',
        };
      }
      let canEdit = true;
      if (user.role === UserRole.Client) {
        canEdit = false;
      }
      if (user.role === UserRole.Owner) {
        if (status !== OrderStatus.Cooking && status !== OrderStatus.Cooked) {
          canEdit = false;
        }
      }
      if (user.role === UserRole.Delivery) {
        if (
          status !== OrderStatus.PickedUp &&
          status !== OrderStatus.Deliverd
        ) {
          canEdit = false;
        }
      }
      if (!canEdit) {
        return {
          ok: false,
          error: 'You can not do that',
        };
      }

      await this.orders.save({
        id: orderId,
        status,
      });
      const newOrder = { ...order, status };
      if (user.role === UserRole.Owner) {
        if (status === OrderStatus.Cooked) {
          await this.pubSub.publish(NEW_COOKED_ORDER, {
            cookedOrders: newOrder,
          });
        }
      }
      await this.pubSub.publish(NEW_ORDER_UPDATE, {
        orderUpdates: newOrder,
      });
      return {
        ok: true,
      };
    } catch {
      return {
        ok: false,
        error: 'Could not edit order',
      };
    }
  }
  

!!!!takeOrder(orders.service.ts)

async takeOrder(
  driver: User,
  { id: orderId }: TakeOrderInput,
): Promise<TakeOrderOutput> {
  try {
    const order = await this.orders.findOne({
      where: {
        id: orderId,
      },
    });
    if (!order) {
      return {
        ok: false,
        error: 'Order not found',
      };
    }
    if (order.driver) {
    ///order에 driver가 있으면, 
      return {
        ok: false,
        error: 'This order already has a driver',
      };
    }
    await this.orders.save({
      id: orderId,
      driver,
    });
    await this.pubSub.publish(NEW_ORDER_UPDATE, {
      orderUpdates: { ...order, driver },
    });
    return {
      ok: true,
    };
  } catch {
    return {
      ok: false,
      error: 'Could not update order',
    };
  }
}
}

!!!NOTICE
이것으로 driver Dashboard는 마무리.
challenge는 꼭 도전해본다.
주소를 입력받아 좌표로 바꾸고 지도에 찍어보는것!!

profile
코딩하는초딩쌤

0개의 댓글