Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PDA-528] Contacts - edit - save to phone #3

Merged
merged 8 commits into from
Nov 30, 2020
Merged
Show file tree
Hide file tree
Changes from 7 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
2 changes: 1 addition & 1 deletion src/backend/adapters/phonebook/phonebook-adapter.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
export default abstract class PhonebookAdapter {
public abstract getContacts(): Promise<DeviceResponse<Contact[]>>
public abstract addContact(contact: NewContact): Promise<DeviceResponse<Contact>>
public abstract editContact(contact: Contact): DeviceResponse<Contact>
public abstract editContact(contact: Contact): Promise<DeviceResponse<Contact>>
public abstract deleteContacts(
contactsIds: ContactID[]
): DeviceResponse<ContactID[]>
Expand Down
2 changes: 1 addition & 1 deletion src/backend/adapters/phonebook/phonebook-fake.adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class PhonebookFake extends PhonebookAdapter {
}
}

public editContact(contact: Contact): DeviceResponse<Contact> {
public async editContact(contact: Contact): Promise<DeviceResponse<Contact>> {
return {
status: DeviceResponseStatus.Ok,
data: contact,
Expand Down
16 changes: 12 additions & 4 deletions src/backend/adapters/phonebook/phonebook.adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,18 @@ class Phonebook extends PhonebookAdapter {
}
}

public editContact(contact: Contact): DeviceResponse<Contact> {
return {
status: DeviceResponseStatus.Ok,
data: contact,
public async editContact(contact: Contact): Promise<DeviceResponse<Contact>> {
const { status } = await this.deviceService.request({
endpoint: Endpoint.Contacts,
method: Method.Post,
body: mapToPureContact(contact),
})

if (status === DeviceResponseStatus.Ok) {
// TODO: return contact from API response after EGD fix, task https://appnroll.atlassian.net/browse/PDA-577
return {status, data: contact}
} else {
return { status, error: { message: "Something went wrong" } }
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/backend/mock-device-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ class MockPureNodeService extends DeviceService {
data: mockPureData,
status: DeviceResponseStatus.Ok,
}
} else if (endpoint === Endpoint.Contacts && method === Method.Post) {
return {
data: mockPureData,
status: DeviceResponseStatus.Ok,
}
} else {
return {
status: DeviceResponseStatus.Error,
Expand Down
2 changes: 1 addition & 1 deletion src/backend/requests/phonebook/edit-contact.request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import DeviceResponse from "Backend/adapters/device-response.interface"
const handleEditContactRequest = (
{ phonebook }: Adapters,
contact: Contact
): DeviceResponse<Contact> => {
): Promise<DeviceResponse<Contact>> => {
return phonebook.editContact(contact)
}

Expand Down
20 changes: 11 additions & 9 deletions src/backend/requests/phonebook/edit-contact.test.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import getFakeAdapters from "App/tests/get-fake-adapters"
import { ipcMain } from "electron-better-ipc"
import { IpcRequest } from "Common/requests/ipc-request.enum"
import { DeviceResponseStatus } from "Backend/adapters/device-response.interface"
import registerEditContactRequest from "Backend/requests/phonebook/edit-contact.request"
import { phoneSeed } from "App/seeds/phone"
import { adapters } from "Backend/requests/phonebook/phonebook-adapters"
import { contact } from "Backend/mock-device-service"

const fakeContact = phoneSeed.db[0]
jest.mock("pure")

test("edit contact properly", () => {
registerEditContactRequest(getFakeAdapters())
test("edit contact properly", async () => {
registerEditContactRequest(adapters)

const [result] = (ipcMain as any)._flush(IpcRequest.EditContact, fakeContact)
const [pendingResponse] = await (ipcMain as any)._flush(
IpcRequest.EditContact,
contact
)
const { data } = await pendingResponse

expect(result.data).toStrictEqual(fakeContact)
expect(result.status).toBe(DeviceResponseStatus.Ok)
expect(data).toMatchObject(contact)
})
1 change: 1 addition & 0 deletions src/renderer/locales/default/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,7 @@
"view.name.phone.contacts.modal.delete.text": "Do you really want to delete\n<b>the {name} contact?</b>",
"view.name.phone.contacts.modal.delete.title": "Delete contact",
"view.name.phone.contacts.modal.deleteMultipleContacts": "Do you really want to delete <b>{num, plural, =-1 {all contacts} one {the {name} contact} other {# contacts}}</b>?",
"view.name.phone.contacts.modal.editing.text": "Editing contact",
"view.name.phone.contacts.modal.speedDial.cancelButton": "Cancel",
"view.name.phone.contacts.modal.speedDial.contactLabel": "Contact",
"view.name.phone.contacts.modal.speedDial.saveButton": "Save",
Expand Down
7 changes: 3 additions & 4 deletions src/renderer/models/phone/phone.helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,8 @@ export const removeContact = (
return { db, collection }
}

export const editContact = (
export const updateContact = (
state: Phone,
id: ContactID,
data: BaseContactModel,
guard: (input: any) => boolean = contactTypeGuard
): Phone => {
Expand All @@ -156,8 +155,8 @@ export const editContact = (
...state,
db: {
...state.db,
[id]: {
...state.db[id],
[data.id]: {
...state.db[data.id],
...data,
},
},
Expand Down
11 changes: 6 additions & 5 deletions src/renderer/models/phone/phone.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
contactDatabaseFactory,
addContacts,
removeContact,
editContact,
updateContact,
findContact,
} from "Renderer/models/phone/phone.helpers"
import { Contact, ContactID } from "Renderer/models/phone/phone.typings"
Expand Down Expand Up @@ -149,7 +149,8 @@ describe("contactDatabaseFactory and mergeContacts tests", () => {

expect(OLD_DB_SHAPE.db[ID_TO_EDIT].firstName).not.toBe(NEW_NAME)

const result = editContact(OLD_DB_SHAPE, ID_TO_EDIT, {
const result = updateContact(OLD_DB_SHAPE, {
id: ID_TO_EDIT,
firstName: NEW_NAME,
})

Expand Down Expand Up @@ -201,7 +202,7 @@ describe("redux tests", () => {
expect(store.getState().phone.collection.indexOf(testId) === -1).toBeFalsy()
})

test("properly changes contact", () => {
test("properly changes contact",() => {
const testName = "some random name"
const modifiedContact = {
...TEST_CONTACT,
Expand All @@ -212,7 +213,7 @@ describe("redux tests", () => {
modifiedContact
)

store.dispatch.phone.editContact([TEST_CONTACT.id], modifiedContact)
store.dispatch.phone.updateContact(modifiedContact)

expect(store.getState().phone.db[TEST_CONTACT.id]).toMatchObject(
modifiedContact
Expand Down Expand Up @@ -255,7 +256,7 @@ describe("redux tests", () => {
store.getState().phone.db[contactWithSpeedDial as ContactID].speedDial
).toBe(speedDial)

store.dispatch.phone.editContact(contactToEdit.id, {
store.dispatch.phone.updateContact({
...contactToEdit,
speedDial,
})
Expand Down
35 changes: 23 additions & 12 deletions src/renderer/models/phone/phone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
import {
addContacts,
contactDatabaseFactory,
editContact,
updateContact,
getFlatList,
getSortedContactList,
getSpeedDialChosenList,
Expand All @@ -22,8 +22,10 @@ import {
import { isContactMatchingPhoneNumber } from "Renderer/models/phone/is-contact-matching-phone-number"
import externalProvidersStore from "Renderer/store/external-providers"
import { Provider } from "Renderer/models/external-providers/external-providers.interface"
import getContacts from "Renderer/requests/get-contacts.request"
import addContact from "Renderer/requests/add-contact.request"
import getContactsRequest from "Renderer/requests/get-contacts.request"
import addContactRequest from "Renderer/requests/add-contact.request"
import editContactRequest from "Renderer/requests/edit-contact.request"
import logger from "App/main/utils/logger"

export const initialState: Phone = {
db: {},
Expand Down Expand Up @@ -81,9 +83,8 @@ export default {

return addContacts(currentState, contact)
},
editContact(
updateContact(
state: Phone,
contactID: ContactID,
data: BaseContactModel
): Phone {
let currentState = state
Expand All @@ -92,7 +93,7 @@ export default {
currentState = revokeField(state, { speedDial: data.speedDial })
}

return editContact(currentState, contactID, data)
return updateContact(currentState, data)
},
removeContact(state: Phone, input: ContactID | ContactID[]): Phone {
return removeContact(state, input)
Expand All @@ -110,8 +111,9 @@ export default {
*/
effects: (dispatch: Dispatch) => ({
loadData: async (): Promise<string | void> => {
const { data = [], error } = await getContacts()
const { data = [], error } = await getContactsRequest()
if (error) {
logger.error(error)
return error.message
} else {
dispatch.phone.setContacts(data)
Expand All @@ -127,16 +129,25 @@ export default {
}
},
addNewContact: async (contact: NewContact): Promise<string | void> => {
const { data, error } = await addContact(contact)
if (error || !data) return error?.message ?? "Something went wrong"
const { data, error } = await addContactRequest(contact)
if (error || !data) {
logger.error(error)
return error?.message ?? "Something went wrong"
}
else {
dispatch.phone.addContact(data)
}
},
async editContact() {
await simulateWriteToPhone()
editContact: async (contact: Contact): Promise<string | void> => {
const { data, error } = await editContactRequest(contact)
if (error || !data) {
logger.error(error)
return error?.message ?? "Something went wrong"
}
else {
dispatch.phone.updateContact(data)
}
},

async removeContact() {
await simulateWriteToPhone()
},
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/models/phone/phone.typings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Provider } from "Renderer/models/external-providers/external-providers.
export type ContactID = string

export interface BaseContactModel {
id?: ContactID
id: ContactID
firstName?: string
lastName?: string
primaryPhoneNumber?: string
Expand Down Expand Up @@ -87,7 +87,7 @@ interface StoreSelectors extends Contacts {
interface StoreEffects {
readonly loadData: () => Promise<string | void>
readonly addNewContact: (contact: NewContact) => Promise<string | void>
readonly editContact?: (id: ContactID, data: Contact) => void
readonly editContact: (data: Contact) => Promise<string | void>
readonly deleteContacts?: (contacts: Contact[]) => void
readonly loadContacts: (provider: Provider) => Promise<Phone>
}
Expand Down
61 changes: 49 additions & 12 deletions src/renderer/modules/phone/phone.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,13 @@ import {
ErrorWithRetryDataModal,
LoadingStateDataModal,
} from "Renderer/components/rest/data-modal/data.modals"
import logger from "App/main/utils/logger"

export const messages = defineMessages({
deleteTitle: { id: "view.name.phone.contacts.modal.delete.title" },
deleteText: { id: "view.name.phone.contacts.modal.delete.text" },
addingText: { id: "view.name.phone.contacts.modal.adding.text" },
editingText: { id: "view.name.phone.contacts.modal.editing.text" },
})

export type PhoneProps = ContactActions &
Expand Down Expand Up @@ -220,7 +222,7 @@ const Phone: FunctionComponent<PhoneProps> = (props) => {
true
)

const error = await addNewContact(contact)
const error = await delayResponse(addNewContact(contact))

if (error && !retried) {
modalService.openModal(
Expand Down Expand Up @@ -249,12 +251,42 @@ const Phone: FunctionComponent<PhoneProps> = (props) => {
openSidebar(contact as Contact)
}

const saveEditedContact = (contact: Contact) => {
setEditedContact(contact)
cancelEditingContact(contact)
openSidebar(contact)
if (editContact) {
editContact(contact.id, contact)
const editContactWithRetry = async (contact: Contact) => {
return new Promise((resolve, reject) => {
const edit = async (retried?: boolean) => {
modalService.openModal(
<LoadingStateDataModal textMessage={messages.editingText} />,
true
)

const error = await delayResponse(editContact(contact))

if (error && !retried) {
modalService.openModal(
<ErrorWithRetryDataModal onRetry={() => edit(true)} />,
true
)
} else if (error) {
modalService.openModal(<ErrorDataModal />, true)
reject()
} else {
await modalService.closeModal()
resolve()
}
}

edit()
})
}

const saveEditedContact = async (contact: Contact) => {
try {
await editContactWithRetry(contact)
setEditedContact(contact)
cancelEditingContact(contact)
openSidebar(contact)
} catch (error) {
logger.error(error)
}
}

Expand Down Expand Up @@ -302,9 +334,12 @@ const Phone: FunctionComponent<PhoneProps> = (props) => {
...contact,
blocked: false,
}
if (editContact) {
await editContact(unblockedContact.id, unblockedContact)
try {
await editContactWithRetry(unblockedContact)
} catch (error) {
logger.error(error)
}

if (detailsEnabled) {
openSidebar(unblockedContact)
}
Expand All @@ -320,10 +355,12 @@ const Phone: FunctionComponent<PhoneProps> = (props) => {
blocked: true,
favourite: false,
}
if (editContact) {
await editContact(blockedContact.id, blockedContact)

try {
await editContactWithRetry(blockedContact)
} catch (error) {
logger.error(error)
}
await modalService.closeModal()

if (detailsEnabled) {
openSidebar(blockedContact)
Expand Down
1 change: 1 addition & 0 deletions src/renderer/modules/phone/phone.container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const mapDispatch = ({ phone, auth }: any) => {
onSpeedDialSettingsSave: noop,
loadData: phone.loadData,
addNewContact: phone.addNewContact,
editContact: phone.editContact,
}
}

Expand Down
1 change: 1 addition & 0 deletions src/renderer/modules/phone/phone.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ const PhoneComponent = ({
isTopicThreadOpened={isTopicThreadOpened}
loadData={asyncNoop}
addNewContact={asyncNoop}
editContact={asyncNoop}
contacts={phoneSeedInput}
loadContacts={asyncNoop}
inputValue={""}
Expand Down
Loading