diff --git a/app/actions/definitions/teams.tsx b/app/actions/definitions/teams.tsx index 06bcbbbb7c..3eae35035f 100644 --- a/app/actions/definitions/teams.tsx +++ b/app/actions/definitions/teams.tsx @@ -1,7 +1,9 @@ import { PlusIcon } from "outline-icons"; import * as React from "react"; import styled from "styled-components"; +import { stringToColor } from "@shared/utils/color"; import TeamNew from "~/scenes/TeamNew"; +import TeamLogo from "~/components/TeamLogo"; import { createAction } from "~/actions"; import { loadSessionsFromCookie } from "~/hooks/useSessions"; import { TeamSection } from "../sections"; @@ -11,7 +13,18 @@ export const switchTeamList = getSessions().map((session) => { name: session.name, section: TeamSection, keywords: "change switch workspace organization team", - icon: () => , + icon: () => ( + + ), visible: ({ currentTeamId }) => currentTeamId !== session.teamId, perform: () => (window.location.href = session.url), }); @@ -55,10 +68,9 @@ function getSessions(params?: { exclude?: string }) { return otherSessions; } -const Logo = styled("img")` +const StyledTeamLogo = styled(TeamLogo)` border-radius: 2px; - width: 24px; - height: 24px; + border: 0; `; export const rootTeamActions = [switchTeam, createTeam]; diff --git a/app/components/Avatar/Avatar.tsx b/app/components/Avatar/Avatar.tsx index 5d26fc8ff6..4d694718f8 100644 --- a/app/components/Avatar/Avatar.tsx +++ b/app/components/Avatar/Avatar.tsx @@ -1,14 +1,21 @@ import * as React from "react"; import styled from "styled-components"; -import User from "~/models/User"; import useBoolean from "~/hooks/useBoolean"; +import Initials from "./Initials"; import placeholder from "./placeholder.png"; +export interface IAvatar { + avatarUrl: string | null; + color: string; + initial: string; + id: string; +} + type Props = { - src: string; size: number; + src?: string; icon?: React.ReactNode; - user?: User; + model?: IAvatar; alt?: string; showBorder?: boolean; onClick?: React.MouseEventHandler; @@ -16,20 +23,28 @@ type Props = { }; function Avatar(props: Props) { - const { src, icon, showBorder, ...rest } = props; - + const { icon, showBorder, model, ...rest } = props; + const src = props.src || model?.avatarUrl; const [error, handleError] = useBoolean(false); return ( - - + + {src ? ( + + ) : model ? ( + + {model.initial} + + ) : ( + + )} {icon && {icon}} - + ); } @@ -37,7 +52,7 @@ Avatar.defaultProps = { size: 24, }; -const AvatarWrapper = styled.div` +const Relative = styled.div` position: relative; `; diff --git a/app/components/Avatar/AvatarWithPresence.tsx b/app/components/Avatar/AvatarWithPresence.tsx index a66278d7a4..7cbe5c5bd8 100644 --- a/app/components/Avatar/AvatarWithPresence.tsx +++ b/app/components/Avatar/AvatarWithPresence.tsx @@ -51,7 +51,7 @@ function AvatarWithPresence({ $isObserving={isObserving} $color={user.color} > - + diff --git a/app/components/Avatar/Initials.tsx b/app/components/Avatar/Initials.tsx new file mode 100644 index 0000000000..bb46dfef76 --- /dev/null +++ b/app/components/Avatar/Initials.tsx @@ -0,0 +1,27 @@ +import styled from "styled-components"; +import Flex from "~/components/Flex"; + +const Initials = styled(Flex)<{ + color?: string; + size: number; + $showBorder?: boolean; +}>` + align-items: center; + justify-content: center; + border-radius: 50%; + width: 100%; + height: 100%; + color: #fff; + background-color: ${(props) => props.color}; + width: ${(props) => props.size}px; + height: ${(props) => props.size}px; + border-radius: 50%; + border: 2px solid + ${(props) => + props.$showBorder === false ? "transparent" : props.theme.background}; + flex-shrink: 0; + font-size: ${(props) => props.size / 2}px; + font-weight: 500; +`; + +export default Initials; diff --git a/app/components/DocumentViews.tsx b/app/components/DocumentViews.tsx index 62ec57d909..a836e0545d 100644 --- a/app/components/DocumentViews.tsx +++ b/app/components/DocumentViews.tsx @@ -43,10 +43,10 @@ function DocumentViews({ document, isOpen }: Props) { { - const view = documentViews.find((v) => v.user.id === item.id); - const isPresent = presentIds.includes(item.id); - const isEditing = editingIds.includes(item.id); + renderItem={(model: User) => { + const view = documentViews.find((v) => v.user.id === model.id); + const isPresent = presentIds.includes(model.id); + const isEditing = editingIds.includes(model.id); const subtitle = isPresent ? isEditing ? t("Currently editing") @@ -58,10 +58,10 @@ function DocumentViews({ document, isOpen }: Props) { }); return ( } + image={} border={false} small /> diff --git a/app/components/EventListItem.tsx b/app/components/EventListItem.tsx index ff22994a7e..a9fdaac831 100644 --- a/app/components/EventListItem.tsx +++ b/app/components/EventListItem.tsx @@ -142,7 +142,7 @@ const EventListItem = ({ event, latest, document, ...rest }: Props) => { onClick={handleTimeClick} /> } - image={} + image={} subtitle={ {icon} diff --git a/app/components/Facepile.tsx b/app/components/Facepile.tsx index a3e117edec..9bd058d21b 100644 --- a/app/components/Facepile.tsx +++ b/app/components/Facepile.tsx @@ -39,7 +39,7 @@ function Facepile({ } function DefaultAvatar(user: User) { - return ; + return ; } const AvatarWrapper = styled.div` diff --git a/app/components/Sidebar/App.tsx b/app/components/Sidebar/App.tsx index fad3164a3f..4385bf0a79 100644 --- a/app/components/Sidebar/App.tsx +++ b/app/components/Sidebar/App.tsx @@ -63,14 +63,7 @@ function AppSidebar() { - } + image={} showDisclosure /> )} @@ -139,11 +132,6 @@ function AppSidebar() { ); } -const StyledTeamLogo = styled(TeamLogo)` - margin-right: 4px; - background: white; -`; - const Drafts = styled(Text)` margin: 0 4px; `; diff --git a/app/components/Sidebar/Sidebar.tsx b/app/components/Sidebar/Sidebar.tsx index e02d10bd5d..9aca1a545f 100644 --- a/app/components/Sidebar/Sidebar.tsx +++ b/app/components/Sidebar/Sidebar.tsx @@ -178,7 +178,7 @@ const Sidebar = React.forwardRef( image={ diff --git a/app/components/TeamLogo.ts b/app/components/TeamLogo.ts index 4df19eafa8..6cadfd4255 100644 --- a/app/components/TeamLogo.ts +++ b/app/components/TeamLogo.ts @@ -1,10 +1,7 @@ import styled from "styled-components"; +import Avatar from "./Avatar"; -const TeamLogo = styled.img<{ width?: number; height?: number; size?: string }>` - width: ${(props) => - props.width ? `${props.width}px` : props.size || "auto"}; - height: ${(props) => - props.height ? `${props.height}px` : props.size || "38px"}; +const TeamLogo = styled(Avatar)` border-radius: 4px; border: 1px solid ${(props) => props.theme.divider}; overflow: hidden; diff --git a/app/models/Team.ts b/app/models/Team.ts index fd2be58856..02248a6bee 100644 --- a/app/models/Team.ts +++ b/app/models/Team.ts @@ -1,5 +1,6 @@ import { computed, observable } from "mobx"; import { TeamPreference, TeamPreferences } from "@shared/types"; +import { stringToColor } from "@shared/utils/color"; import BaseModel from "./BaseModel"; import Field from "./decorators/Field"; @@ -69,6 +70,16 @@ class Team extends BaseModel { return "SSO"; } + @computed + get color(): string { + return stringToColor(this.id); + } + + @computed + get initial(): string { + return this.name ? this.name[0] : "?"; + } + /** * Returns whether this team is using a separate editing mode behind an "Edit" * button rather than seamless always-editing. diff --git a/app/models/User.ts b/app/models/User.ts index ceb7372dba..5d70623db5 100644 --- a/app/models/User.ts +++ b/app/models/User.ts @@ -40,6 +40,11 @@ class User extends ParanoidModel { isSuspended: boolean; + @computed + get initial(): string { + return this.name ? this.name[0] : "?"; + } + @computed get isInvited(): boolean { return !this.lastActiveAt; diff --git a/app/scenes/Collection/MembershipPreview.tsx b/app/scenes/Collection/MembershipPreview.tsx index 36f08b5337..f3e74ccf83 100644 --- a/app/scenes/Collection/MembershipPreview.tsx +++ b/app/scenes/Collection/MembershipPreview.tsx @@ -104,18 +104,16 @@ const MembershipPreview = ({ collection, limit = 8 }: Props) => { users={sortBy(collectionUsers, "lastActiveAt")} overflow={overflow} limit={limit} - renderAvatar={(user) => ( - - )} + renderAvatar={(user) => } /> ); }; -const StyledAvatar = styled(Avatar)<{ user: User }>` +const StyledAvatar = styled(Avatar)<{ model: User }>` transition: opacity 250ms ease-in-out; - opacity: ${(props) => (props.user.isRecentlyActive ? 1 : 0.5)}; + opacity: ${(props) => (props.model.isRecentlyActive ? 1 : 0.5)}; `; export default observer(MembershipPreview); diff --git a/app/scenes/CollectionPermissions/components/MemberListItem.tsx b/app/scenes/CollectionPermissions/components/MemberListItem.tsx index 06a7d0c24b..3176a21b5f 100644 --- a/app/scenes/CollectionPermissions/components/MemberListItem.tsx +++ b/app/scenes/CollectionPermissions/components/MemberListItem.tsx @@ -49,7 +49,7 @@ const MemberListItem = ({ {user.isAdmin && {t("Admin")}} } - image={} + image={} actions={ {onUpdate && ( diff --git a/app/scenes/CollectionPermissions/components/UserListItem.tsx b/app/scenes/CollectionPermissions/components/UserListItem.tsx index 925fea75a8..eecd5ae593 100644 --- a/app/scenes/CollectionPermissions/components/UserListItem.tsx +++ b/app/scenes/CollectionPermissions/components/UserListItem.tsx @@ -21,7 +21,7 @@ const UserListItem = ({ user, onAdd, canEdit }: Props) => { return ( } + image={} subtitle={ <> {user.lastActiveAt ? ( diff --git a/app/scenes/GroupMembers/components/GroupMemberListItem.tsx b/app/scenes/GroupMembers/components/GroupMemberListItem.tsx index f470585936..3c7a1fad5e 100644 --- a/app/scenes/GroupMembers/components/GroupMemberListItem.tsx +++ b/app/scenes/GroupMembers/components/GroupMemberListItem.tsx @@ -35,7 +35,7 @@ const GroupMemberListItem = ({ user, onRemove, onAdd }: Props) => { {user.isAdmin && {t("Admin")}} } - image={} + image={} actions={ {onRemove && } diff --git a/app/scenes/GroupMembers/components/UserListItem.tsx b/app/scenes/GroupMembers/components/UserListItem.tsx index 925fea75a8..eecd5ae593 100644 --- a/app/scenes/GroupMembers/components/UserListItem.tsx +++ b/app/scenes/GroupMembers/components/UserListItem.tsx @@ -21,7 +21,7 @@ const UserListItem = ({ user, onAdd, canEdit }: Props) => { return ( } + image={} subtitle={ <> {user.lastActiveAt ? ( diff --git a/app/scenes/Login/index.tsx b/app/scenes/Login/index.tsx index dab5eeb862..81adf346e4 100644 --- a/app/scenes/Login/index.tsx +++ b/app/scenes/Login/index.tsx @@ -169,7 +169,7 @@ function Login({ children }: Props) { /> {config.logo ? ( - + ) : ( )} diff --git a/app/scenes/Settings/Details.tsx b/app/scenes/Settings/Details.tsx index b45a5e185a..3f2e769ea9 100644 --- a/app/scenes/Settings/Details.tsx +++ b/app/scenes/Settings/Details.tsx @@ -26,7 +26,6 @@ function Details() { const form = useRef(null); const [name, setName] = useState(team.name); const [subdomain, setSubdomain] = useState(team.subdomain); - const [avatarUrl, setAvatarUrl] = useState(team.avatarUrl); const [defaultCollectionId, setDefaultCollectionId] = useState( team.defaultCollectionId ); @@ -40,7 +39,6 @@ function Details() { try { await auth.updateTeam({ name, - avatarUrl, subdomain, defaultCollectionId, }); @@ -53,7 +51,7 @@ function Details() { }); } }, - [auth, name, avatarUrl, subdomain, defaultCollectionId, showToast, t] + [auth, name, subdomain, defaultCollectionId, showToast, t] ); const handleNameChange = React.useCallback( @@ -71,7 +69,6 @@ function Details() { ); const handleAvatarUpload = async (avatarUrl: string) => { - setAvatarUrl(avatarUrl); await auth.updateTeam({ avatarUrl, }); @@ -115,7 +112,7 @@ function Details() { diff --git a/app/scenes/Settings/Profile.tsx b/app/scenes/Settings/Profile.tsx index 2bd29a1ed1..bc804be060 100644 --- a/app/scenes/Settings/Profile.tsx +++ b/app/scenes/Settings/Profile.tsx @@ -18,7 +18,6 @@ const Profile = () => { const user = useCurrentUser(); const form = React.useRef(null); const [name, setName] = React.useState(user.name || ""); - const [avatarUrl, setAvatarUrl] = React.useState(user.avatarUrl); const { showToast } = useToasts(); const { t } = useTranslation(); @@ -28,7 +27,6 @@ const Profile = () => { try { await auth.updateUser({ name, - avatarUrl, }); showToast(t("Profile saved"), { type: "success", @@ -45,7 +43,6 @@ const Profile = () => { }; const handleAvatarUpload = async (avatarUrl: string) => { - setAvatarUrl(avatarUrl); await auth.updateUser({ avatarUrl, }); @@ -79,7 +76,7 @@ const Profile = () => { - - + + {t("Upload")} @@ -28,8 +29,8 @@ const avatarStyles = ` height: 64px; `; -const Avatar = styled.img` - ${avatarStyles}; +const StyledAvatar = styled(Avatar)` + border-radius: 8px; `; const ImageBox = styled(Flex)` @@ -41,7 +42,7 @@ const ImageBox = styled(Flex)` background: ${(props) => props.theme.background}; overflow: hidden; - div div { + .upload { ${avatarStyles}; position: absolute; top: 0; @@ -53,7 +54,7 @@ const ImageBox = styled(Flex)` transition: all 250ms; } - &:hover div { + &:hover .upload { opacity: 1; background: rgba(0, 0, 0, 0.75); color: ${(props) => props.theme.white}; diff --git a/app/scenes/Settings/components/PeopleTable.tsx b/app/scenes/Settings/components/PeopleTable.tsx index 00485fc8a7..1910725ef2 100644 --- a/app/scenes/Settings/components/PeopleTable.tsx +++ b/app/scenes/Settings/components/PeopleTable.tsx @@ -29,7 +29,7 @@ function PeopleTable({ canManage, ...rest }: Props) { Cell: observer( ({ value, row }: { value: string; row: { original: User } }) => ( - {value}{" "} + {value}{" "} {currentUser.id === row.original.id && `(${t("You")})`} ) diff --git a/app/scenes/Settings/components/SharesTable.tsx b/app/scenes/Settings/components/SharesTable.tsx index 29f551378a..8532db7167 100644 --- a/app/scenes/Settings/components/SharesTable.tsx +++ b/app/scenes/Settings/components/SharesTable.tsx @@ -39,7 +39,7 @@ function SharesTable({ canManage, ...rest }: Props) { {row.original.createdBy && ( )} diff --git a/app/scenes/Settings/components/UserListItem.tsx b/app/scenes/Settings/components/UserListItem.tsx index 45d707cef6..c4cba3bfd4 100644 --- a/app/scenes/Settings/components/UserListItem.tsx +++ b/app/scenes/Settings/components/UserListItem.tsx @@ -20,7 +20,7 @@ const UserListItem = ({ user, showMenu }: Props) => { return ( {user.name}} - image={} + image={} subtitle={ <> {user.email ? `${user.email} · ` : undefined} diff --git a/app/scenes/UserProfile.tsx b/app/scenes/UserProfile.tsx index 0a5014f80d..2e95ed360e 100644 --- a/app/scenes/UserProfile.tsx +++ b/app/scenes/UserProfile.tsx @@ -39,7 +39,7 @@ function UserProfile(props: Props) { - +  {user.name} } diff --git a/app/stores/AuthStore.ts b/app/stores/AuthStore.ts index c2601b4953..8cc3e3e9bf 100644 --- a/app/stores/AuthStore.ts +++ b/app/stores/AuthStore.ts @@ -226,14 +226,17 @@ export default class AuthStore { preferences?: UserPreferences; }) => { this.isSaving = true; + const previousData = this.user?.toAPI(); try { + this.user?.updateFromJson(params); const res = await client.post(`/users.update`, params); invariant(res?.data, "User response not available"); - runInAction("AuthStore#updateUser", () => { - this.addPolicies(res.policies); - this.user = new User(res.data, this); - }); + this.user?.updateFromJson(res.data); + this.addPolicies(res.policies); + } catch (err) { + this.user?.updateFromJson(previousData); + throw err; } finally { this.isSaving = false; } @@ -251,14 +254,17 @@ export default class AuthStore { preferences?: TeamPreferences; }) => { this.isSaving = true; + const previousData = this.team?.toAPI(); try { + this.team?.updateFromJson(params); const res = await client.post(`/team.update`, params); invariant(res?.data, "Team response not available"); - runInAction("AuthStore#updateTeam", () => { - this.addPolicies(res.policies); - this.team = new Team(res.data, this); - }); + this.team?.updateFromJson(res.data); + this.addPolicies(res.policies); + } catch (err) { + this.team?.updateFromJson(previousData); + throw err; } finally { this.isSaving = false; } diff --git a/server/commands/teamCreator.ts b/server/commands/teamCreator.ts index a5e6e7b484..c0b3a89cbf 100644 --- a/server/commands/teamCreator.ts +++ b/server/commands/teamCreator.ts @@ -40,7 +40,6 @@ async function teamCreator({ // one via ClearBit, or fallback to colored initials in worst case scenario if (!avatarUrl || !avatarUrl.startsWith("http")) { avatarUrl = await generateAvatarUrl({ - name, domain, id: subdomain, }); diff --git a/server/env.ts b/server/env.ts index 1d17631947..673967b9e8 100644 --- a/server/env.ts +++ b/server/env.ts @@ -329,13 +329,6 @@ export class Environment { */ public RELEASE = this.toOptionalString(process.env.RELEASE); - /** - * An optional host from which to load default avatars. - */ - @IsUrl() - public DEFAULT_AVATAR_HOST = - process.env.DEFAULT_AVATAR_HOST ?? "https://tiley.herokuapp.com"; - /** * A Google Analytics tracking ID, only v3 supported at this time. */ diff --git a/server/models/Team.ts b/server/models/Team.ts index 3bbd86ed04..dc76684234 100644 --- a/server/models/Team.ts +++ b/server/models/Team.ts @@ -24,7 +24,6 @@ import { CollectionPermission, TeamPreference } from "@shared/types"; import { getBaseDomain, RESERVED_SUBDOMAINS } from "@shared/utils/domains"; import env from "@server/env"; import DeleteAttachmentTask from "@server/queues/tasks/DeleteAttachmentTask"; -import { generateAvatarUrl } from "@server/utils/avatars"; import parseAttachmentIds from "@server/utils/parseAttachmentIds"; import Attachment from "./Attachment"; import AuthenticationProvider from "./AuthenticationProvider"; @@ -94,8 +93,20 @@ class Team extends ParanoidModel { @AllowNull @IsUrl @Length({ max: 4096, msg: "avatarUrl must be 4096 characters or less" }) - @Column - avatarUrl: string | null; + @Column(DataType.STRING) + get avatarUrl() { + const original = this.getDataValue("avatarUrl"); + + if (original && !original.startsWith("https://tiley.herokuapp.com")) { + return original; + } + + return null; + } + + set avatarUrl(value: string | null) { + this.setDataValue("avatarUrl", value); + } @Default(true) @Column @@ -163,16 +174,6 @@ class Team extends ParanoidModel { return url.href.replace(/\/$/, ""); } - get logoUrl() { - return ( - this.avatarUrl || - generateAvatarUrl({ - id: this.id, - name: this.name, - }) - ); - } - /** * Preferences that decide behavior for the team. * diff --git a/server/models/User.ts b/server/models/User.ts index 1cf03eb471..76ca484289 100644 --- a/server/models/User.ts +++ b/server/models/User.ts @@ -180,17 +180,11 @@ class User extends ParanoidModel { get avatarUrl() { const original = this.getDataValue("avatarUrl"); - if (original) { + if (original && !original.startsWith("https://tiley.herokuapp.com")) { return original; } - const color = this.color.replace(/^#/, ""); - const initial = this.name ? this.name[0] : "?"; - const hash = crypto - .createHash("md5") - .update(this.email || "") - .digest("hex"); - return `${env.DEFAULT_AVATAR_HOST}/avatar/${hash}/${initial}.png?c=${color}`; + return null; } set avatarUrl(value: string | null) { diff --git a/server/presenters/__snapshots__/user.test.ts.snap b/server/presenters/__snapshots__/user.test.ts.snap index 05e431ffdf..808d3d6306 100644 --- a/server/presenters/__snapshots__/user.test.ts.snap +++ b/server/presenters/__snapshots__/user.test.ts.snap @@ -2,7 +2,7 @@ exports[`presents a user 1`] = ` Object { - "avatarUrl": "https://tiley.herokuapp.com/avatar/d41d8cd98f00b204e9800998ecf8427e/T.png?c=FF5C80", + "avatarUrl": null, "color": "#FF5C80", "createdAt": undefined, "id": "123", @@ -17,7 +17,7 @@ Object { exports[`presents a user without slack data 1`] = ` Object { - "avatarUrl": "https://tiley.herokuapp.com/avatar/d41d8cd98f00b204e9800998ecf8427e/T.png?c=FF5C80", + "avatarUrl": null, "color": "#FF5C80", "createdAt": undefined, "id": "123", diff --git a/server/presenters/availableTeam.ts b/server/presenters/availableTeam.ts index 175345800b..1b0f1274d2 100644 --- a/server/presenters/availableTeam.ts +++ b/server/presenters/availableTeam.ts @@ -4,7 +4,7 @@ export default function present(team: Team, isSignedIn = false) { return { id: team.id, name: team.name, - avatarUrl: team.logoUrl, + avatarUrl: team.avatarUrl, url: team.url, isSignedIn, }; diff --git a/server/presenters/team.ts b/server/presenters/team.ts index 74748dd475..1420828106 100644 --- a/server/presenters/team.ts +++ b/server/presenters/team.ts @@ -4,7 +4,7 @@ export default function present(team: Team) { return { id: team.id, name: team.name, - avatarUrl: team.logoUrl, + avatarUrl: team.avatarUrl, sharing: team.sharing, memberCollectionCreate: team.memberCollectionCreate, collaborativeEditing: team.collaborativeEditing, diff --git a/server/routes/api/__snapshots__/users.test.ts.snap b/server/routes/api/__snapshots__/users.test.ts.snap index 9b8e275d94..f19021c13e 100644 --- a/server/routes/api/__snapshots__/users.test.ts.snap +++ b/server/routes/api/__snapshots__/users.test.ts.snap @@ -3,7 +3,7 @@ exports[`#users.activate should activate a suspended user 1`] = ` Object { "data": Object { - "avatarUrl": "https://tiley.herokuapp.com/avatar/111d68d06e2d317b5a59c2c6c5bad808/U.png?c=e600e0", + "avatarUrl": null, "color": "#e600e0", "createdAt": "2018-01-02T00:00:00.000Z", "email": "user1@example.com", @@ -59,7 +59,7 @@ Object { exports[`#users.demote should demote an admin 1`] = ` Object { "data": Object { - "avatarUrl": "https://tiley.herokuapp.com/avatar/111d68d06e2d317b5a59c2c6c5bad808/U.png?c=e600e0", + "avatarUrl": null, "color": "#e600e0", "createdAt": "2018-01-02T00:00:00.000Z", "email": "user1@example.com", @@ -97,7 +97,7 @@ Object { exports[`#users.demote should demote an admin to member 1`] = ` Object { "data": Object { - "avatarUrl": "https://tiley.herokuapp.com/avatar/111d68d06e2d317b5a59c2c6c5bad808/U.png?c=e600e0", + "avatarUrl": null, "color": "#e600e0", "createdAt": "2018-01-02T00:00:00.000Z", "email": "user1@example.com", @@ -135,7 +135,7 @@ Object { exports[`#users.demote should demote an admin to viewer 1`] = ` Object { "data": Object { - "avatarUrl": "https://tiley.herokuapp.com/avatar/111d68d06e2d317b5a59c2c6c5bad808/U.png?c=e600e0", + "avatarUrl": null, "color": "#e600e0", "createdAt": "2018-01-02T00:00:00.000Z", "email": "user1@example.com", @@ -191,7 +191,7 @@ Object { exports[`#users.promote should promote a new admin 1`] = ` Object { "data": Object { - "avatarUrl": "https://tiley.herokuapp.com/avatar/111d68d06e2d317b5a59c2c6c5bad808/U.png?c=e600e0", + "avatarUrl": null, "color": "#e600e0", "createdAt": "2018-01-02T00:00:00.000Z", "email": "user1@example.com", @@ -256,7 +256,7 @@ Object { exports[`#users.suspend should suspend an user 1`] = ` Object { "data": Object { - "avatarUrl": "https://tiley.herokuapp.com/avatar/111d68d06e2d317b5a59c2c6c5bad808/U.png?c=e600e0", + "avatarUrl": null, "color": "#e600e0", "createdAt": "2018-01-02T00:00:00.000Z", "email": "user1@example.com", diff --git a/server/utils/authentication.ts b/server/utils/authentication.ts index a2efde3a85..80bde37c7a 100644 --- a/server/utils/authentication.ts +++ b/server/utils/authentication.ts @@ -93,7 +93,7 @@ export async function signIn( ...existing, [team.id]: { name: team.name, - logoUrl: team.logoUrl, + logoUrl: team.avatarUrl, url: team.url, }, }) diff --git a/server/utils/avatars.test.ts b/server/utils/avatars.test.ts index 1ad196569a..9bffe6c891 100644 --- a/server/utils/avatars.test.ts +++ b/server/utils/avatars.test.ts @@ -4,43 +4,6 @@ it("should return clearbit url if available", async () => { const url = await generateAvatarUrl({ id: "google", domain: "google.com", - name: "Google", }); expect(url).toBe("https://logo.clearbit.com/google.com"); }); -it("should return tiley url if clearbit unavailable", async () => { - const url = await generateAvatarUrl({ - id: "invalid", - domain: "example.invalid", - name: "Invalid", - }); - expect(url).toBe( - "https://tiley.herokuapp.com/avatar/f1234d75178d892a133a410355a5a990cf75d2f33eba25d575943d4df632f3a4/I.png" - ); -}); -it("should return tiley url if domain not provided", async () => { - const url = await generateAvatarUrl({ - id: "google", - name: "Google", - }); - expect(url).toBe( - "https://tiley.herokuapp.com/avatar/bbdefa2950f49882f295b1285d4fa9dec45fc4144bfb07ee6acc68762d12c2e3/G.png" - ); -}); -it("should return tiley url if name not provided", async () => { - const url = await generateAvatarUrl({ - id: "google", - }); - expect(url).toBe( - "https://tiley.herokuapp.com/avatar/bbdefa2950f49882f295b1285d4fa9dec45fc4144bfb07ee6acc68762d12c2e3/U.png" - ); -}); -it("should return tiley url with encoded name", async () => { - const url = await generateAvatarUrl({ - id: "google", - name: "株", - }); - expect(url).toBe( - "https://tiley.herokuapp.com/avatar/bbdefa2950f49882f295b1285d4fa9dec45fc4144bfb07ee6acc68762d12c2e3/%E6%A0%AA.png" - ); -}); diff --git a/server/utils/avatars.ts b/server/utils/avatars.ts index 826b4afb76..d3a9f9644b 100644 --- a/server/utils/avatars.ts +++ b/server/utils/avatars.ts @@ -1,21 +1,17 @@ import crypto from "crypto"; import fetch from "fetch-with-proxy"; -import env from "@server/env"; export async function generateAvatarUrl({ id, domain, - name = "Unknown", }: { id: string; domain?: string; - name?: string; }) { // attempt to get logo from Clearbit API. If one doesn't exist then // fall back to using tiley to generate a placeholder logo const hash = crypto.createHash("sha256"); hash.update(id); - const hashedId = hash.digest("hex"); let cbResponse, cbUrl; if (domain) { @@ -28,8 +24,5 @@ export async function generateAvatarUrl({ } } - const tileyUrl = `${ - env.DEFAULT_AVATAR_HOST - }/avatar/${hashedId}/${encodeURIComponent(name[0])}.png`; - return cbUrl && cbResponse && cbResponse.status === 200 ? cbUrl : tileyUrl; + return cbUrl && cbResponse && cbResponse.status === 200 ? cbUrl : null; } diff --git a/server/utils/s3.ts b/server/utils/s3.ts index 0e21b80956..fa898f861d 100644 --- a/server/utils/s3.ts +++ b/server/utils/s3.ts @@ -6,7 +6,6 @@ import fetch from "fetch-with-proxy"; import { compact } from "lodash"; import { useAgent } from "request-filtering-agent"; import { v4 as uuidv4 } from "uuid"; -import env from "@server/env"; import Logger from "@server/logging/Logger"; const AWS_S3_ACCELERATE_URL = process.env.AWS_S3_ACCELERATE_URL; @@ -184,11 +183,7 @@ export const uploadToS3FromUrl = async ( acl: string ) => { const endpoint = publicS3Endpoint(true); - if ( - url.startsWith("/api") || - url.startsWith(endpoint) || - url.startsWith(env.DEFAULT_AVATAR_HOST) - ) { + if (url.startsWith("/api") || url.startsWith(endpoint)) { return; }