Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3353368
D10583.id35436.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
21 KB
Referenced Files
None
Subscribers
None
D10583.id35436.diff
View Options
diff --git a/lib/keyserver-conn/keyserver-conn-types.js b/lib/keyserver-conn/keyserver-conn-types.js
new file mode 100644
--- /dev/null
+++ b/lib/keyserver-conn/keyserver-conn-types.js
@@ -0,0 +1,51 @@
+// @flow
+
+import type {
+ LogInActionSource,
+ LogInStartingPayload,
+ LogInResult,
+} from '../types/account-types.js';
+import type { Dispatch } from '../types/redux-types.js';
+import type {
+ ClientSessionChange,
+ PreRequestUserState,
+} from '../types/session-types.js';
+
+export type ActionTypes<
+ STARTED_ACTION_TYPE: string,
+ SUCCESS_ACTION_TYPE: string,
+ FAILED_ACTION_TYPE: string,
+> = {
+ started: STARTED_ACTION_TYPE,
+ success: SUCCESS_ACTION_TYPE,
+ failed: FAILED_ACTION_TYPE,
+};
+
+export type DispatchRecoveryAttempt = (
+ actionTypes: ActionTypes<'LOG_IN_STARTED', 'LOG_IN_SUCCESS', 'LOG_IN_FAILED'>,
+ promise: Promise<LogInResult>,
+ startingPayload: LogInStartingPayload,
+) => Promise<boolean>;
+
+const setNewSessionActionType = 'SET_NEW_SESSION';
+function setNewSession(
+ dispatch: Dispatch,
+ sessionChange: ClientSessionChange,
+ preRequestUserState: ?PreRequestUserState,
+ error: ?string,
+ logInActionSource: ?LogInActionSource,
+ keyserverID: string,
+) {
+ dispatch({
+ type: setNewSessionActionType,
+ payload: {
+ sessionChange,
+ preRequestUserState,
+ error,
+ logInActionSource,
+ keyserverID,
+ },
+ });
+}
+
+export { setNewSessionActionType, setNewSession };
diff --git a/lib/reducers/calendar-filters-reducer.js b/lib/reducers/calendar-filters-reducer.js
--- a/lib/reducers/calendar-filters-reducer.js
+++ b/lib/reducers/calendar-filters-reducer.js
@@ -17,6 +17,7 @@
logInActionTypes,
keyserverRegisterActionTypes,
} from '../actions/user-actions.js';
+import { setNewSessionActionType } from '../keyserver-conn/keyserver-conn-types.js';
import {
filteredThreadIDs,
nonThreadCalendarFilters,
@@ -42,7 +43,6 @@
type ClientUpdateInfo,
processUpdatesActionType,
} from '../types/update-types.js';
-import { setNewSessionActionType } from '../utils/action-utils.js';
import { filterThreadIDsBelongingToCommunity } from '../utils/drawer-utils.react.js';
export default function reduceCalendarFilters(
diff --git a/lib/reducers/calendar-query-reducer.js b/lib/reducers/calendar-query-reducer.js
--- a/lib/reducers/calendar-query-reducer.js
+++ b/lib/reducers/calendar-query-reducer.js
@@ -8,6 +8,7 @@
logInActionTypes,
keyserverRegisterActionTypes,
} from '../actions/user-actions.js';
+import { setNewSessionActionType } from '../keyserver-conn/keyserver-conn-types.js';
import { defaultCalendarQuery } from '../types/entry-types.js';
import type { CalendarQuery } from '../types/entry-types.js';
import { type BaseAction } from '../types/redux-types.js';
@@ -15,7 +16,6 @@
fullStateSyncActionType,
incrementalStateSyncActionType,
} from '../types/socket-types.js';
-import { setNewSessionActionType } from '../utils/action-utils.js';
import { getConfig } from '../utils/config.js';
function reduceCalendarQuery(
diff --git a/lib/reducers/data-loaded-reducer.js b/lib/reducers/data-loaded-reducer.js
--- a/lib/reducers/data-loaded-reducer.js
+++ b/lib/reducers/data-loaded-reducer.js
@@ -6,8 +6,8 @@
deleteKeyserverAccountActionTypes,
logInActionTypes,
} from '../actions/user-actions.js';
+import { setNewSessionActionType } from '../keyserver-conn/keyserver-conn-types.js';
import type { BaseAction } from '../types/redux-types.js';
-import { setNewSessionActionType } from '../utils/action-utils.js';
export default function reduceDataLoaded(
state: boolean,
diff --git a/lib/reducers/draft-reducer.js b/lib/reducers/draft-reducer.js
--- a/lib/reducers/draft-reducer.js
+++ b/lib/reducers/draft-reducer.js
@@ -9,9 +9,9 @@
deleteKeyserverAccountActionTypes,
logOutActionTypes,
} from '../actions/user-actions.js';
+import { setNewSessionActionType } from '../keyserver-conn/keyserver-conn-types.js';
import type { DraftStore, DraftStoreOperation } from '../types/draft-types.js';
import type { BaseAction } from '../types/redux-types.js';
-import { setNewSessionActionType } from '../utils/action-utils.js';
type ReduceDraftStoreResult = {
+draftStoreOperations: $ReadOnlyArray<DraftStoreOperation>,
diff --git a/lib/reducers/enabled-apps-reducer.js b/lib/reducers/enabled-apps-reducer.js
--- a/lib/reducers/enabled-apps-reducer.js
+++ b/lib/reducers/enabled-apps-reducer.js
@@ -4,13 +4,13 @@
deleteKeyserverAccountActionTypes,
logOutActionTypes,
} from '../actions/user-actions.js';
+import { setNewSessionActionType } from '../keyserver-conn/keyserver-conn-types.js';
import type { EnabledApps } from '../types/enabled-apps.js';
import {
defaultEnabledApps,
defaultWebEnabledApps,
} from '../types/enabled-apps.js';
import type { BaseAction } from '../types/redux-types.js';
-import { setNewSessionActionType } from '../utils/action-utils.js';
export const enableAppActionType = 'ENABLE_APP';
export const disableAppActionType = 'DISABLE_APP';
diff --git a/lib/reducers/entry-reducer.js b/lib/reducers/entry-reducer.js
--- a/lib/reducers/entry-reducer.js
+++ b/lib/reducers/entry-reducer.js
@@ -39,6 +39,7 @@
deleteKeyserverAccountActionTypes,
logInActionTypes,
} from '../actions/user-actions.js';
+import { setNewSessionActionType } from '../keyserver-conn/keyserver-conn-types.js';
import { entryID } from '../shared/entry-utils.js';
import { stateSyncSpecs } from '../shared/state-sync/state-sync-specs.js';
import { threadInFilterList } from '../shared/thread-utils.js';
@@ -59,7 +60,6 @@
type ClientUpdateInfo,
processUpdatesActionType,
} from '../types/update-types.js';
-import { setNewSessionActionType } from '../utils/action-utils.js';
import { dateString } from '../utils/date-utils.js';
import { values } from '../utils/objects.js';
diff --git a/lib/reducers/invite-links-reducer.js b/lib/reducers/invite-links-reducer.js
--- a/lib/reducers/invite-links-reducer.js
+++ b/lib/reducers/invite-links-reducer.js
@@ -9,9 +9,9 @@
deleteKeyserverAccountActionTypes,
logOutActionTypes,
} from '../actions/user-actions.js';
+import { setNewSessionActionType } from '../keyserver-conn/keyserver-conn-types.js';
import type { InviteLinksStore, CommunityLinks } from '../types/link-types.js';
import type { BaseAction } from '../types/redux-types.js';
-import { setNewSessionActionType } from '../utils/action-utils.js';
function reduceInviteLinks(
state: InviteLinksStore,
diff --git a/lib/reducers/keyserver-reducer.js b/lib/reducers/keyserver-reducer.js
--- a/lib/reducers/keyserver-reducer.js
+++ b/lib/reducers/keyserver-reducer.js
@@ -18,6 +18,7 @@
logInActionTypes,
resetUserStateActionType,
} from '../actions/user-actions.js';
+import { setNewSessionActionType } from '../keyserver-conn/keyserver-conn-types.js';
import { queueActivityUpdatesActionType } from '../types/activity-types.js';
import type { KeyserverInfos, KeyserverStore } from '../types/keyserver-types';
import type { BaseAction } from '../types/redux-types.js';
@@ -31,7 +32,6 @@
} from '../types/socket-types.js';
import { updateTypes } from '../types/update-types-enum.js';
import { processUpdatesActionType } from '../types/update-types.js';
-import { setNewSessionActionType } from '../utils/action-utils.js';
import { getConfig } from '../utils/config.js';
import { setURLPrefix } from '../utils/url-utils.js';
import { ashoatKeyserverID } from '../utils/validation-utils.js';
diff --git a/lib/reducers/loading-reducer.js b/lib/reducers/loading-reducer.js
--- a/lib/reducers/loading-reducer.js
+++ b/lib/reducers/loading-reducer.js
@@ -2,9 +2,9 @@
import _omit from 'lodash/fp/omit.js';
+import type { ActionTypes } from '../keyserver-conn/keyserver-conn-types.js';
import type { LoadingStatus } from '../types/loading-types.js';
import type { BaseAction } from '../types/redux-types.js';
-import type { ActionTypes } from '../utils/action-utils.js';
const fetchKeyRegistry: Set<string> = new Set();
const registerFetchKey = (actionTypes: ActionTypes<*, *, *>) => {
diff --git a/lib/reducers/message-reducer.js b/lib/reducers/message-reducer.js
--- a/lib/reducers/message-reducer.js
+++ b/lib/reducers/message-reducer.js
@@ -52,6 +52,7 @@
logInActionTypes,
keyserverRegisterActionTypes,
} from '../actions/user-actions.js';
+import { setNewSessionActionType } from '../keyserver-conn/keyserver-conn-types.js';
import {
messageStoreOpsHandlers,
type MessageStoreOperation,
@@ -99,7 +100,6 @@
type ClientUpdateInfo,
processUpdatesActionType,
} from '../types/update-types.js';
-import { setNewSessionActionType } from '../utils/action-utils.js';
import { translateClientDBThreadMessageInfos } from '../utils/message-ops-utils.js';
const _mapValuesWithKeys = _mapValues.convert({ cap: false });
diff --git a/lib/reducers/report-store-reducer.js b/lib/reducers/report-store-reducer.js
--- a/lib/reducers/report-store-reducer.js
+++ b/lib/reducers/report-store-reducer.js
@@ -12,6 +12,7 @@
deleteKeyserverAccountActionTypes,
logInActionTypes,
} from '../actions/user-actions.js';
+import { setNewSessionActionType } from '../keyserver-conn/keyserver-conn-types.js';
import type { ReportStoreOperation } from '../ops/report-store-ops.js';
import {
convertReportsToRemoveReportsOperation,
@@ -26,7 +27,6 @@
defaultDevEnabledReports,
type ClientReportCreationRequest,
} from '../types/report-types.js';
-import { setNewSessionActionType } from '../utils/action-utils.js';
import { isDev } from '../utils/dev-utils.js';
import { isReportEnabled } from '../utils/report-utils.js';
diff --git a/lib/reducers/services-access-token-reducer.js b/lib/reducers/services-access-token-reducer.js
--- a/lib/reducers/services-access-token-reducer.js
+++ b/lib/reducers/services-access-token-reducer.js
@@ -5,8 +5,8 @@
deleteKeyserverAccountActionTypes,
setAccessTokenActionType,
} from '../actions/user-actions.js';
+import { setNewSessionActionType } from '../keyserver-conn/keyserver-conn-types.js';
import type { BaseAction } from '../types/redux-types.js';
-import { setNewSessionActionType } from '../utils/action-utils.js';
export default function reduceServicesAccessToken(
state: ?string,
diff --git a/lib/reducers/theme-reducer.js b/lib/reducers/theme-reducer.js
--- a/lib/reducers/theme-reducer.js
+++ b/lib/reducers/theme-reducer.js
@@ -8,12 +8,12 @@
logInActionTypes,
keyserverRegisterActionTypes,
} from '../actions/user-actions.js';
+import { setNewSessionActionType } from '../keyserver-conn/keyserver-conn-types.js';
import type { BaseAction } from '../types/redux-types.js';
import {
defaultGlobalThemeInfo,
type GlobalThemeInfo,
} from '../types/theme-types.js';
-import { setNewSessionActionType } from '../utils/action-utils.js';
export default function reduceGlobalThemeInfo(
state: GlobalThemeInfo,
diff --git a/lib/reducers/thread-activity-reducer.js b/lib/reducers/thread-activity-reducer.js
--- a/lib/reducers/thread-activity-reducer.js
+++ b/lib/reducers/thread-activity-reducer.js
@@ -18,6 +18,7 @@
logOutActionTypes,
deleteKeyserverAccountActionTypes,
} from '../actions/user-actions.js';
+import { setNewSessionActionType } from '../keyserver-conn/keyserver-conn-types.js';
import type { BaseAction } from '../types/redux-types.js';
import { incrementalStateSyncActionType } from '../types/socket-types.js';
import type { ThreadActivityStore } from '../types/thread-activity-types.js';
@@ -25,7 +26,6 @@
import { updateTypes } from '../types/update-types-enum.js';
import type { ClientUpdateInfo } from '../types/update-types.js';
import { processUpdatesActionType } from '../types/update-types.js';
-import { setNewSessionActionType } from '../utils/action-utils.js';
function reduceThreadActivity(
state: ThreadActivityStore,
diff --git a/lib/reducers/thread-reducer.js b/lib/reducers/thread-reducer.js
--- a/lib/reducers/thread-reducer.js
+++ b/lib/reducers/thread-reducer.js
@@ -25,6 +25,7 @@
keyserverRegisterActionTypes,
updateSubscriptionActionTypes,
} from '../actions/user-actions.js';
+import { setNewSessionActionType } from '../keyserver-conn/keyserver-conn-types.js';
import {
type ThreadStoreOperation,
threadStoreOpsHandlers,
@@ -50,7 +51,6 @@
type ClientUpdateInfo,
processUpdatesActionType,
} from '../types/update-types.js';
-import { setNewSessionActionType } from '../utils/action-utils.js';
const { processStoreOperations: processThreadStoreOperations } =
threadStoreOpsHandlers;
diff --git a/lib/reducers/user-reducer.js b/lib/reducers/user-reducer.js
--- a/lib/reducers/user-reducer.js
+++ b/lib/reducers/user-reducer.js
@@ -18,6 +18,7 @@
updateUserAvatarActionTypes,
resetUserStateActionType,
} from '../actions/user-actions.js';
+import { setNewSessionActionType } from '../keyserver-conn/keyserver-conn-types.js';
import {
convertUserInfosToReplaceUserOps,
type UserStoreOperation,
@@ -43,7 +44,6 @@
UserInfos,
UserStore,
} from '../types/user-types.js';
-import { setNewSessionActionType } from '../utils/action-utils.js';
import { getMessageForException } from '../utils/errors.js';
import { assertObjectsAreEqual } from '../utils/objects.js';
diff --git a/lib/selectors/loading-selectors.js b/lib/selectors/loading-selectors.js
--- a/lib/selectors/loading-selectors.js
+++ b/lib/selectors/loading-selectors.js
@@ -6,10 +6,10 @@
import _memoize from 'lodash/memoize.js';
import { createSelector } from 'reselect';
+import type { ActionTypes } from '../keyserver-conn/keyserver-conn-types.js';
import { registerFetchKey } from '../reducers/loading-reducer.js';
import type { LoadingStatus } from '../types/loading-types.js';
import type { BaseAppState } from '../types/redux-types.js';
-import type { ActionTypes } from '../utils/action-utils.js';
import { values } from '../utils/objects.js';
function loadingStatusFromInfo(loadingStatusInfo: {
diff --git a/lib/socket/socket.react.js b/lib/socket/socket.react.js
--- a/lib/socket/socket.react.js
+++ b/lib/socket/socket.react.js
@@ -15,6 +15,7 @@
import UpdateHandler from './update-handler.react.js';
import { updateActivityActionTypes } from '../actions/activity-actions.js';
import { updateLastCommunicatedPlatformDetailsActionType } from '../actions/device-actions.js';
+import { setNewSessionActionType } from '../keyserver-conn/keyserver-conn-types.js';
import { unsupervisedBackgroundActionType } from '../reducers/lifecycle-state-reducer.js';
import {
pingFrequency,
@@ -65,10 +66,7 @@
} from '../types/socket-types.js';
import { actionLogger } from '../utils/action-logger.js';
import type { DispatchActionPromise } from '../utils/action-utils.js';
-import {
- setNewSessionActionType,
- resolveKeyserverSessionInvalidation,
-} from '../utils/action-utils.js';
+import { resolveKeyserverSessionInvalidation } from '../utils/action-utils.js';
import { getConfig } from '../utils/config.js';
import { ServerError, SocketTimeout, SocketOffline } from '../utils/errors.js';
import { promiseAll } from '../utils/promises.js';
diff --git a/lib/utils/action-utils.js b/lib/utils/action-utils.js
--- a/lib/utils/action-utils.js
+++ b/lib/utils/action-utils.js
@@ -15,6 +15,10 @@
import { useSelector, useDispatch } from './redux-utils.js';
import { usingCommServicesAccessToken } from './services-utils.js';
import { ashoatKeyserverID } from './validation-utils.js';
+import {
+ type ActionTypes,
+ setNewSession,
+} from '../keyserver-conn/keyserver-conn-types.js';
import { serverCallStateSelector } from '../selectors/server-calls.js';
import {
logInActionSources,
@@ -31,24 +35,11 @@
PromisedAction,
BaseAction,
} from '../types/redux-types.js';
-import type {
- ClientSessionChange,
- PreRequestUserState,
-} from '../types/session-types.js';
+import type { ClientSessionChange } from '../types/session-types.js';
import type { CurrentUserInfo } from '../types/user-types.js';
let nextPromiseIndex = 0;
-export type ActionTypes<
- STARTED_ACTION_TYPE: string,
- SUCCESS_ACTION_TYPE: string,
- FAILED_ACTION_TYPE: string,
-> = {
- started: STARTED_ACTION_TYPE,
- success: SUCCESS_ACTION_TYPE,
- failed: FAILED_ACTION_TYPE,
-};
-
function wrapActionPromise<
STARTED_ACTION_TYPE: string,
STARTED_PAYLOAD: ActionPayload,
@@ -153,33 +144,6 @@
callServerEndpoint: ?CallServerEndpoint,
) => void)[] = [];
-export type DispatchRecoveryAttempt = (
- actionTypes: ActionTypes<'LOG_IN_STARTED', 'LOG_IN_SUCCESS', 'LOG_IN_FAILED'>,
- promise: Promise<LogInResult>,
- startingPayload: LogInStartingPayload,
-) => Promise<boolean>;
-
-const setNewSessionActionType = 'SET_NEW_SESSION';
-function setNewSession(
- dispatch: Dispatch,
- sessionChange: ClientSessionChange,
- preRequestUserState: ?PreRequestUserState,
- error: ?string,
- logInActionSource: ?LogInActionSource,
- keyserverID: string,
-) {
- dispatch({
- type: setNewSessionActionType,
- payload: {
- sessionChange,
- preRequestUserState,
- error,
- logInActionSource,
- keyserverID,
- },
- });
-}
-
// This function is a shortcut that tells us whether it's worth even trying to
// call resolveKeyserverSessionInvalidation
function canResolveKeyserverSessionInvalidation() {
@@ -537,7 +501,6 @@
export {
useDispatchActionPromise,
- setNewSessionActionType,
resolveKeyserverSessionInvalidation,
createBoundServerCallsSelector,
registerActiveSocket,
diff --git a/lib/utils/config.js b/lib/utils/config.js
--- a/lib/utils/config.js
+++ b/lib/utils/config.js
@@ -2,9 +2,9 @@
import invariant from 'invariant';
-import type { DispatchRecoveryAttempt } from './action-utils.js';
import type { CallServerEndpoint } from './call-server-endpoint.js';
import type { CallKeyserverEndpoint } from './keyserver-call.js';
+import type { DispatchRecoveryAttempt } from '../keyserver-conn/keyserver-conn-types.js';
import type { LogInActionSource } from '../types/account-types.js';
import type { PlatformDetails } from '../types/device-types.js';
diff --git a/lib/utils/sanitization.js b/lib/utils/sanitization.js
--- a/lib/utils/sanitization.js
+++ b/lib/utils/sanitization.js
@@ -3,8 +3,8 @@
import clone from 'just-clone';
import stringHash from 'string-hash';
-import { setNewSessionActionType } from './action-utils.js';
import { setDeviceTokenActionTypes } from '../actions/device-actions.js';
+import { setNewSessionActionType } from '../keyserver-conn/keyserver-conn-types.js';
import type { BaseAction, AppState } from '../types/redux-types.js';
export type ReduxCrashReport = {
diff --git a/native/account/resolve-invalidated-cookie.js b/native/account/resolve-invalidated-cookie.js
--- a/native/account/resolve-invalidated-cookie.js
+++ b/native/account/resolve-invalidated-cookie.js
@@ -1,9 +1,9 @@
// @flow
import { logInActionTypes, logInRawAction } from 'lib/actions/user-actions.js';
+import type { DispatchRecoveryAttempt } from 'lib/keyserver-conn/keyserver-conn-types.js';
import type { InitialNotifMessageOptions } from 'lib/shared/crypto-utils.js';
import type { LogInActionSource } from 'lib/types/account-types.js';
-import type { DispatchRecoveryAttempt } from 'lib/utils/action-utils.js';
import type { CallServerEndpoint } from 'lib/utils/call-server-endpoint.js';
import type { CallKeyserverEndpoint } from 'lib/utils/keyserver-call.js';
diff --git a/native/redux/redux-setup.js b/native/redux/redux-setup.js
--- a/native/redux/redux-setup.js
+++ b/native/redux/redux-setup.js
@@ -12,6 +12,7 @@
deleteKeyserverAccountActionTypes,
logInActionTypes,
} from 'lib/actions/user-actions.js';
+import { setNewSessionActionType } from 'lib/keyserver-conn/keyserver-conn-types.js';
import type { ThreadStoreOperation } from 'lib/ops/thread-store-ops.js';
import { threadStoreOpsHandlers } from 'lib/ops/thread-store-ops.js';
import { reduceLoadingStatuses } from 'lib/reducers/loading-reducer.js';
@@ -25,7 +26,6 @@
import { rehydrateActionType } from 'lib/types/redux-types.js';
import type { SetSessionPayload } from 'lib/types/session-types.js';
import { reduxLoggerMiddleware } from 'lib/utils/action-logger.js';
-import { setNewSessionActionType } from 'lib/utils/action-utils.js';
import { ashoatKeyserverID } from 'lib/utils/validation-utils.js';
import {
diff --git a/web/redux/crypto-store-reducer.js b/web/redux/crypto-store-reducer.js
--- a/web/redux/crypto-store-reducer.js
+++ b/web/redux/crypto-store-reducer.js
@@ -4,8 +4,8 @@
logOutActionTypes,
deleteKeyserverAccountActionTypes,
} from 'lib/actions/user-actions.js';
+import { setNewSessionActionType } from 'lib/keyserver-conn/keyserver-conn-types.js';
import type { CryptoStore } from 'lib/types/crypto-types.js';
-import { setNewSessionActionType } from 'lib/utils/action-utils.js';
import type { Action } from './redux-setup.js';
diff --git a/web/redux/redux-setup.js b/web/redux/redux-setup.js
--- a/web/redux/redux-setup.js
+++ b/web/redux/redux-setup.js
@@ -7,6 +7,7 @@
logOutActionTypes,
deleteKeyserverAccountActionTypes,
} from 'lib/actions/user-actions.js';
+import { setNewSessionActionType } from 'lib/keyserver-conn/keyserver-conn-types.js';
import {
type ThreadStoreOperation,
threadStoreOpsHandlers,
@@ -35,7 +36,6 @@
import type { ThreadActivityStore } from 'lib/types/thread-activity-types';
import type { ThreadStore } from 'lib/types/thread-types.js';
import type { CurrentUserInfo, UserStore } from 'lib/types/user-types.js';
-import { setNewSessionActionType } from 'lib/utils/action-utils.js';
import type { NotifPermissionAlertInfo } from 'lib/utils/push-alerts.js';
import { ashoatKeyserverID } from 'lib/utils/validation-utils.js';
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Nov 24, 9:36 AM (21 h, 34 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2574851
Default Alt Text
D10583.id35436.diff (21 KB)
Attached To
Mode
D10583: [lib][native][web] Move some types and basic functions out of action-utils.js
Attached
Detach File
Event Timeline
Log In to Comment