Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const viewport = 'desktop';

const member: ProfileType = {
name: 'member',
backgroundColor: '#f090b3',
description: 'A member profile',
displayName: 'Member',
id: 2,
Expand All @@ -27,6 +28,7 @@ const member: ProfileType = {

const space: ProfileType = {
name: 'space',
backgroundColor: '#f090b3',
description: 'A space profile',
displayName: 'Space',
id: 3,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export default {

const member: ProfileType = {
name: 'member',
backgroundColor: '#f090b3',
description: 'A member profile',
displayName: 'Member',
id: 2,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type { ProfileType } from 'oa-shared';

const member: ProfileType = {
name: 'member',
backgroundColor: '#f090b3',
description: 'A member profile',
displayName: 'Member',
id: 2,
Expand Down
2 changes: 2 additions & 0 deletions packages/components/src/MemberBadge/MemberBadge.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export default {
} as Meta<typeof MemberBadge>;
const machineBuilder: ProfileType = {
name: 'machine-builder',
backgroundColor: '#f29195',
description: 'A machine builder profile',
displayName: 'Machine Builder',
id: 1,
Expand All @@ -26,6 +27,7 @@ const machineBuilder: ProfileType = {
};
const member: ProfileType = {
name: 'member',
backgroundColor: '#f090b3',
description: 'A member profile',
displayName: 'Member',
id: 2,
Expand Down
1 change: 1 addition & 0 deletions packages/components/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ export const fakeResearchUpdate = (

export const fakeProfileType = (profileTypeOverLoads: Partial<ProfileType> = {}): ProfileType => ({
id: faker.number.int(),
backgroundColor: '#f090b3',
description: '',
displayName: '',
imageUrl: faker.image.avatar(),
Expand Down
5 changes: 5 additions & 0 deletions seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ const seedTags = (): tagsChildInputs => [{ ..._TAGS_BASE, name: 'tag 1' }];
const seedProfileTypes = (): Partial<profile_typesScalars>[] => [
{
..._TYPES_BASE,
background_color: '#f090b3',
name: 'member',
display_name: 'Member',
is_space: false,
Expand All @@ -119,6 +120,7 @@ const seedProfileTypes = (): Partial<profile_typesScalars>[] => [
},
{
..._TYPES_BASE,
background_color: '#97cdeb',
name: 'workspace',
display_name: 'Workspace',
is_space: true,
Expand All @@ -132,6 +134,7 @@ const seedProfileTypes = (): Partial<profile_typesScalars>[] => [
},
{
..._TYPES_BASE,
background_color: '#f29195',
name: 'machine-builder',
display_name: 'Machine Builder',
is_space: true,
Expand All @@ -145,6 +148,7 @@ const seedProfileTypes = (): Partial<profile_typesScalars>[] => [
},
{
..._TYPES_BASE,
background_color: '#8ec685',
name: 'community-point',
display_name: 'Community Point',
is_space: true,
Expand All @@ -158,6 +162,7 @@ const seedProfileTypes = (): Partial<profile_typesScalars>[] => [
},
{
..._TYPES_BASE,
background_color: '#baa0cc',
name: 'collection-point',
display_name: 'Collection Point',
is_space: true,
Expand Down
5 changes: 5 additions & 0 deletions shared/mocks/data/profileTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { DBProfileType } from '../../models/profileType';
export const profileTypes: Partial<DBProfileType>[] = [
{
name: 'member',
background_color: '#f090b3',
display_name: 'Member',
is_space: false,
description: '',
Expand All @@ -15,6 +16,7 @@ export const profileTypes: Partial<DBProfileType>[] = [
},
{
name: 'machine-builder',
background_color: '#f29195',
display_name: 'Machine Builder',
order: 3,
description: '',
Expand All @@ -27,6 +29,7 @@ export const profileTypes: Partial<DBProfileType>[] = [
},
{
name: 'workspace',
background_color: '#97cdeb',
display_name: 'Workspace',
order: 2,
description: '',
Expand All @@ -39,6 +42,7 @@ export const profileTypes: Partial<DBProfileType>[] = [
},
{
name: 'community-point',
background_color: '#8ec685',
display_name: 'Community Point',
order: 4,
description: '',
Expand All @@ -51,6 +55,7 @@ export const profileTypes: Partial<DBProfileType>[] = [
},
{
name: 'collection-point',
background_color: '#baa0cc',
display_name: 'Collection Point',
order: 5,
description: '',
Expand Down
3 changes: 3 additions & 0 deletions shared/models/profileType.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export class DBProfileType {
id: number;
background_color: string;
description: string;
display_name: string;
image_url: string;
Expand All @@ -16,6 +17,7 @@ export class DBProfileType {

export class ProfileType {
id: number;
backgroundColor: string;
description: string;
displayName: string;
imageUrl: string;
Expand All @@ -32,6 +34,7 @@ export class ProfileType {
static fromDB(value: DBProfileType) {
return new ProfileType({
id: value.id,
backgroundColor: value.background_color,
description: value.description,
displayName: value.display_name,
imageUrl: value.image_url,
Expand Down
50 changes: 47 additions & 3 deletions src/pages/Maps/Content/MapView/Cluster.client.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import L from 'leaflet';
import { type MarkerCluster, MarkerClusterGroup } from 'leaflet.markercluster';
import type { MapPin } from 'oa-shared';
import { type RefObject, useEffect, useRef } from 'react';
import { type RefObject, useEffect, useRef, useState } from 'react';
import { useMap } from 'react-leaflet';
import { createClusterIcon, createMarkerIcon } from './Sprites';

Expand All @@ -10,6 +10,7 @@ interface IProps {
onPinClick: (pin: MapPin) => void;
onClusterClick: (cluster: MarkerCluster) => void;
clusterGroupRef?: RefObject<any>;
selectedPin?: MapPin | null;
}

/**
Expand All @@ -19,7 +20,12 @@ interface IProps {
*
* @see https://github.com/Leaflet/Leaflet.markercluster#clusters-methods
*/
export const Clusters = ({ pins, onPinClick, onClusterClick, clusterGroupRef }: IProps) => {
export const Clusters = (props: IProps) => {
const { pins, onPinClick, onClusterClick, clusterGroupRef, selectedPin } = props;
const [previousSelectedPin, setPreviousSelectedPin] = useState<MapPin | null>(
selectedPin || null,
);

const map = useMap();
const groupRef = useRef<MarkerClusterGroup | null>(null);
const markersRef = useRef<Map<string, L.Marker>>(new Map());
Expand Down Expand Up @@ -69,6 +75,42 @@ export const Clusters = ({ pins, onPinClick, onClusterClick, clusterGroupRef }:
};
}, [map, clusterGroupRef]);

useEffect(() => {
const setSelectedPin = (selectedPin) => {
const currentMarkers = markersRef.current;
const id = String(selectedPin?.id);

if (currentMarkers.has(id) && selectedPin) {
console.log('hello');
currentMarkers.get(id)?.setIcon(createMarkerIcon(selectedPin, true));
}
};

const clearSelectedPin = () => {
const currentMarkers = markersRef.current;
const id = String(previousSelectedPin?.id);

if (currentMarkers.has(id)) {
previousSelectedPin &&
currentMarkers.get(id)?.setIcon(createMarkerIcon(previousSelectedPin, false));
}
};

if (selectedPin && !previousSelectedPin) {
setSelectedPin(selectedPin);
setPreviousSelectedPin(selectedPin);
}
if (selectedPin && selectedPin.id !== previousSelectedPin?.id) {
clearSelectedPin();
setSelectedPin(selectedPin);
setPreviousSelectedPin(selectedPin);
}
if (!selectedPin && previousSelectedPin) {
clearSelectedPin();
setPreviousSelectedPin(null);
}
}, [selectedPin]);

// Differential marker updates — only add/remove changed pins.
useEffect(() => {
const group = groupRef.current;
Expand All @@ -93,9 +135,11 @@ export const Clusters = ({ pins, onPinClick, onClusterClick, clusterGroupRef }:
const toAdd: L.Marker[] = [];
for (const pin of validPins) {
const id = String(pin.id);
const isSelectedPin = pin.id === selectedPin?.id;

if (!currentMarkers.has(id)) {
const marker = L.marker([pin.lat, pin.lng], {
icon: createMarkerIcon(pin),
icon: createMarkerIcon(pin, isSelectedPin),
});
marker.on('click', () => onPinClickRef.current(pin));
currentMarkers.set(id, marker);
Expand Down
7 changes: 6 additions & 1 deletion src/pages/Maps/Content/MapView/MapView.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,18 @@ export const MapView = ({
{mapState.mapPins && mapState.mapPins.length > 0 && (
<>
{disableClusters || !mapState.selectPinWithClusterCheck ? (
<Markers pins={mapState.mapPins} onPinClick={mapState.selectPin} />
<Markers
pins={mapState.mapPins}
onPinClick={mapState.selectPin}
selectedPin={mapState.selectedPin}
/>
) : (
<Clusters
pins={mapState.mapPins}
onPinClick={mapState.selectPinWithClusterCheck}
onClusterClick={handleClusterClick}
clusterGroupRef={clusterGroupRef}
selectedPin={mapState.selectedPin}
/>
)}
</>
Expand Down
7 changes: 5 additions & 2 deletions src/pages/Maps/Content/MapView/Markers.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import { createMarkerIcon } from './Sprites';
interface IProps {
pins: MapPin[];
onPinClick: (pin: MapPin) => void;
selectedPin?: MapPin | null;
}

/**
* Renders individual map pins without clustering.
*/
export const Markers = ({ pins, onPinClick }: IProps) => {
export const Markers = ({ pins, onPinClick, selectedPin }: IProps) => {
const map = useMap();
const markersRef = useRef<Map<string, L.Marker>>(new Map());
const onPinClickRef = useRef(onPinClick);
Expand All @@ -39,9 +40,11 @@ export const Markers = ({ pins, onPinClick }: IProps) => {
// Add new markers
for (const pin of validPins) {
const id = String(pin.id);
const isSelectedPin = pin.id === selectedPin?.id;

if (!currentMarkers.has(id)) {
const marker = L.marker([pin.lat, pin.lng], {
icon: createMarkerIcon(pin),
icon: createMarkerIcon(pin, isSelectedPin),
});
marker.on('click', () => onPinClickRef.current(pin));
marker.addTo(map);
Expand Down
20 changes: 13 additions & 7 deletions src/pages/Maps/Content/MapView/Sprites.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,18 +70,24 @@ export const createClusterIcon = () => {
};
};

export const createMarkerIcon = (pin: MapPin, draggable?: boolean) => {
export const createMarkerIcon = (pin: MapPin, isSelectedPin: boolean) => {
const icon =
pin.moderation === 'accepted'
? pin.profile!.type?.smallImageUrl || clusterIcon
: AwaitingModerationHighlight;
let className = `icon-marker icon-${pin.profile!.type?.name}`;
const shouldShowAnimationStyle = isSelectedPin && pin.profile.type?.backgroundColor;

return divIcon({
className: `icon-marker icon-${pin.profile!.type}`,
html: `<img
data-cy="pin-${pin.profile.username}"
src="${icon}"
style="width: 100%; height: 100%; ${draggable ? 'cursor: grab' : ''}"
/>`,
className,
html: `<div class="${isSelectedPin ? 'icon-marker-selected' : ''}">
<img
data-cy="pin-${pin.profile.username}"
src="${icon}"
style="width: 100%; height: 100%;"
/>
<div class="${shouldShowAnimationStyle ? 'icon-marker-selected-after' : ''}" style="background-color: ${pin.profile.type?.backgroundColor}"></div>
</div>`,
iconSize: point(38, 38, true),
});
};
Expand Down
22 changes: 14 additions & 8 deletions src/pages/Maps/Content/MapView/sprites.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,6 @@
line-height: 26px;
text-align: center;
color: white;
padding-left: 1px;
border-radius: 50%;
}

/* To make sure images are not
bigger than image container */
.icon-marker img {
width: 100%;
}

.icon-cluster-text {
Expand All @@ -19,3 +11,17 @@ bigger than image container */
width: 100%;
height: 100%;
}

.icon-marker .icon-marker-selected {
transform: translateY(-5px);
}

.icon-marker-selected-after {
position: relative;
top: -47px;
bottom: 0;
padding-bottom: 10px;
height: 48px !important;
clip-path: polygon(85% 69%, 51% 100%, 15% 69%, 50% 20%);
z-index: -1;
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ describe('Focus', () => {
profileTypes={[
{
id: 1,
backgroundColor: '#f090b3',
name: 'member',
displayName: 'Member',
description: 'Member desc',
Expand All @@ -50,6 +51,7 @@ describe('Focus', () => {
profileTypes={[
{
id: 1,
backgroundColor: '#f090b3',
name: 'member',
displayName: 'Member',
description: 'Member desc',
Expand All @@ -61,6 +63,7 @@ describe('Focus', () => {
},
{
id: 2,
backgroundColor: '#97cdeb',
name: 'space',
displayName: 'Space',
description: 'space desc',
Expand Down
1 change: 1 addition & 0 deletions src/routes/api.map-pin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export const loader = async ({ request }) => {
),
type:profile_types(
id,
background_color,
name,
display_name,
image_url,
Expand Down
1 change: 1 addition & 0 deletions src/routes/api.map-pins.$userId.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export const loader = async ({ request, params }) => {
),
type:profile_types(
id,
background_color,
name,
display_name,
image_url,
Expand Down
Loading
Loading