Skip to content

Commit

Permalink
Merged logid2.
Browse files Browse the repository at this point in the history
  • Loading branch information
0x7B5 committed Jun 1, 2024
2 parents f1d7928 + dc086af commit e3925d4
Show file tree
Hide file tree
Showing 24 changed files with 409 additions and 232 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ There's a ton of them, but these are worth mentioning because they sort of affec
* `@material-ui` - Lots of fully featured highly customizable components for building the UIs with. Theming system with global and per-component overrides of any CSS values.
* `react-router-redux` - the newer one, 5.x.... Mindlessly simple routing with convenient global access due to redux

<<<<<<< HEAD
## How things works
The current playback is tracked not by storing the current offset, but instead storing the local time that the player began, the offset it began at, and the playback rate. Any time any of these values change, it rebases them all back to the current time. It means that at any arbitrary moment you can calculate the current offset with...
```js
Expand All @@ -32,4 +33,8 @@ The current playback is tracked not by storing the current offset, but instead s

With this central authority on current offset time, it becomes much easier to have each data source keep themselves in sync instead of trying to manage synchronizing all of them.

=======
## Development
`pnpm start`
>>>>>>> upstream/logid2
10 changes: 8 additions & 2 deletions src/actions/history.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { LOCATION_CHANGE } from 'connected-react-router';
import { getDongleID, getZoom, getPrimeNav } from '../url';
import { getDongleID, getZoom, getSegmentRange, getPrimeNav } from '../url';
import { primeNav, selectDevice, pushTimelineRange } from './index';

export const onHistoryMiddleware = ({ dispatch, getState }) => (next) => (action) => {
Expand All @@ -19,7 +19,13 @@ export const onHistoryMiddleware = ({ dispatch, getState }) => (next) => (action

const pathZoom = getZoom(action.payload.location.pathname);
if (pathZoom !== state.zoom) {
dispatch(pushTimelineRange(pathZoom?.start, pathZoom?.end, false));
console.debug("TODO: this should redirect to a log id")
//dispatch(pushTimelineRange(pathZoom?.start, pathZoom?.end, false));
}

const pathSegmentRange = getSegmentRange(action.payload.location.pathname);
if (pathSegmentRange !== state.segmentRange) {
dispatch(pushTimelineRange(pathSegmentRange?.log_id, pathSegmentRange?.start, pathSegmentRange?.end, false));
}

const pathPrimeNav = getPrimeNav(action.payload.location.pathname);
Expand Down
7 changes: 3 additions & 4 deletions src/actions/history.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ describe('history middleware', () => {
};
invoke(action);
expect(next).toHaveBeenCalledWith(action);
expect(store.dispatch).toHaveBeenCalledTimes(1);
expect(store.dispatch).toHaveBeenCalledWith(fakeInner);
expect(actionsIndex.selectDevice).toHaveBeenCalledWith('0000aaaa0000aaaa', false);
});
Expand All @@ -96,14 +95,14 @@ describe('history middleware', () => {
type: LOCATION_CHANGE,
payload: {
action: 'POP',
location: { pathname: '0000aaaa0000aaaa/1230/1234' },
location: { pathname: '0000aaaa0000aaaa/00000014--55a0b1280e/1230/1234' },
},
};
invoke(action);
expect(next).toHaveBeenCalledWith(action);
expect(store.dispatch).toHaveBeenCalledTimes(1);
expect(store.dispatch).toHaveBeenCalledWith(fakeInner);
expect(actionsIndex.pushTimelineRange).toHaveBeenCalledWith(1230, 1234, false);
expect(actionsIndex.pushTimelineRange).toHaveBeenCalledWith('00000014--55a0b1280e', 1230, 1234, false);
});

it('should call prime nav with history', async () => {
Expand All @@ -130,7 +129,7 @@ describe('history middleware', () => {
expect(store.dispatch).toHaveBeenCalledTimes(2);
expect(store.dispatch).toHaveBeenCalledWith(fakeInner);
expect(store.dispatch).toHaveBeenCalledWith(fakeInner2);
expect(actionsIndex.pushTimelineRange).toHaveBeenCalledWith(undefined, undefined, false);
expect(actionsIndex.pushTimelineRange).toHaveBeenCalledWith(undefined, undefined, undefined, false);
expect(actionsIndex.primeNav).toHaveBeenCalledWith(true);
});
});
267 changes: 265 additions & 2 deletions src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,244 @@ import { getDeviceFromState, deviceVersionAtLeast } from '../utils';

let routesRequest = null;

function updateTimeline(state, dispatch, log_id, start, end, allowPathChange) {
dispatch(checkRoutesData());

if (!state.loop || !state.loop.startTime || !state.loop.duration || state.loop.startTime < start
|| state.loop.startTime + state.loop.duration > end || state.loop.duration < end - start) {
dispatch(resetPlayback());
dispatch(selectLoop(start, end));
}

// TODO: fix this up
if (allowPathChange) {
const desiredPath = urlForState(state.dongleId, log_id, start, end, false);
if (window.location.pathname !== desiredPath) {
dispatch(push(desiredPath));
}
}
}

export function popTimelineRange(allowPathChange = true) {
return (dispatch, getState) => {
const state = getState();
if (state.zoom.previous) {
dispatch({
type: Types.TIMELINE_POP_SELECTION,
});

const { start, end } = state.zoom.previous;
updateTimeline(state, dispatch, state.currentRoute?.log_id, start, end, allowPathChange);
}
};
}

export function pushTimelineRange(log_id, start, end, allowPathChange = true) {
return (dispatch, getState) => {
const state = getState();
if (state.zoom?.start !== start || state.zoom?.end !== end) {
dispatch({
type: Types.TIMELINE_PUSH_SELECTION,
log_id,
start,
end,
});
}

updateTimeline(state, dispatch, log_id, start, end, allowPathChange);
};
}

export function selectDevice(dongleId, allowPathChange = true) {
return (dispatch, getState) => {
const state = getState();
let device;
if (state.devices && state.devices.length > 1) {
device = state.devices.find((d) => d.dongle_id === dongleId);
}
if (!device && state.device && state.device.dongle_id === dongleId) {
device = state.device;
}

dispatch({
type: Types.ACTION_SELECT_DEVICE,
dongleId,
});

dispatch(pushTimelineRange(null, null, null, false));
if ((device && !device.shared) || state.profile?.superuser) {
dispatch(primeFetchSubscription(dongleId, device));
dispatch(fetchDeviceOnline(dongleId));
}

dispatch(checkRoutesData());

if (allowPathChange) {
const desiredPath = urlForState(dongleId, null, null, null, null);
if (window.location.pathname !== desiredPath) {
dispatch(push(desiredPath));
}
}
};
}

export function primeFetchSubscription(dongleId, device, profile) {
return (dispatch, getState) => {
const state = getState();

if (!device && state.device && state.device === dongleId) {
device = state.device;
}
if (!profile && state.profile) {
profile = state.profile;
}

if (device && (device.is_owner || profile.superuser)) {
if (device.prime) {
Billing.getSubscription(dongleId).then((subscription) => {
dispatch(primeGetSubscription(dongleId, subscription));
}).catch((err) => {
console.error(err);
Sentry.captureException(err, { fingerprint: 'actions_fetch_subscription' });
});
} else {
Billing.getSubscribeInfo(dongleId).then((subscribeInfo) => {
dispatch({
type: Types.ACTION_PRIME_SUBSCRIBE_INFO,
dongleId,
subscribeInfo,
});
}).catch((err) => {
console.error(err);
Sentry.captureException(err, { fingerprint: 'actions_fetch_subscribe_info' });
});
}
}
};
}

export function primeNav(nav, allowPathChange = true) {
return (dispatch, getState) => {
const state = getState();
if (!state.dongleId) {
return;
}

if (state.primeNav !== nav) {
dispatch({
type: Types.ACTION_PRIME_NAV,
primeNav: nav,
});
}

if (allowPathChange) {
const curPath = document.location.pathname;
const desiredPath = urlForState(state.dongleId, null, null, null, nav);
if (curPath !== desiredPath) {
dispatch(push(desiredPath));
}
}
};
}

export function fetchSharedDevice(dongleId) {
return async (dispatch) => {
try {
const resp = await Devices.fetchDevice(dongleId);
dispatch({
type: Types.ACTION_UPDATE_SHARED_DEVICE,
dongleId,
device: resp,
});
} catch (err) {
if (!err.resp || err.resp.status !== 403) {
console.error(err);
Sentry.captureException(err, { fingerprint: 'action_fetch_shared_device' });
}
}
};
}

export function fetchDeviceOnline(dongleId) {
return (dispatch) => {
Devices.fetchDevice(dongleId).then((resp) => {
dispatch({
type: Types.ACTION_UPDATE_DEVICE_ONLINE,
dongleId,
last_athena_ping: resp.last_athena_ping,
fetched_at: Math.floor(Date.now() / 1000),
});
}).catch(console.log);
};
}

export function updateDeviceOnline(dongleId, lastAthenaPing) {
return (dispatch) => {
dispatch({
type: Types.ACTION_UPDATE_DEVICE_ONLINE,
dongleId,
last_athena_ping: lastAthenaPing,
fetched_at: Math.floor(Date.now() / 1000),
});
};
}

export function fetchDeviceNetworkStatus(dongleId) {
return async (dispatch, getState) => {
const device = getDeviceFromState(getState(), dongleId);
if (deviceVersionAtLeast(device, '0.8.14')) {
const payload = {
id: 0,
jsonrpc: '2.0',
method: 'getNetworkMetered',
};
try {
const resp = await Athena.postJsonRpcPayload(dongleId, payload);
if (resp && resp.result !== undefined) {
dispatch({
type: Types.ACTION_UPDATE_DEVICE_NETWORK,
dongleId,
networkMetered: resp.result,
});
dispatch(updateDeviceOnline(dongleId, Math.floor(Date.now() / 1000)));
}
} catch (err) {
if (err.message && (err.message.indexOf('Timed out') === -1 || err.message.indexOf('Device not registered') === -1)) {
dispatch(updateDeviceOnline(dongleId, 0));
} else {
console.error(err);
Sentry.captureException(err, { fingerprint: 'athena_fetch_networkmetered' });
}
}
} else {
const payload = {
id: 0,
jsonrpc: '2.0',
method: 'getNetworkType',
};
try {
const resp = await Athena.postJsonRpcPayload(dongleId, payload);
if (resp && resp.result !== undefined) {
const metered = resp.result !== 1 && resp.result !== 6; // wifi or ethernet
dispatch({
type: Types.ACTION_UPDATE_DEVICE_NETWORK,
dongleId,
networkMetered: metered,
});
dispatch(updateDeviceOnline(dongleId, Math.floor(Date.now() / 1000)));
}
} catch (err) {
if (err.message && (err.message.indexOf('Timed out') === -1 || err.message.indexOf('Device not registered') === -1)) {
dispatch(updateDeviceOnline(dongleId, 0));
} else {
console.error(err);
Sentry.captureException(err, { fingerprint: 'athena_fetch_networktype' });
}
}
}
};
}

export function checkRoutesData() {
return (dispatch, getState) => {
let state = getState();
Expand Down Expand Up @@ -66,11 +304,12 @@ export function checkRoutesData() {
return {
...r,
url: r.url.replace('chffrprivate.blob.core.windows.net', 'chffrprivate.azureedge.net'),
offset: Math.round(startTime) - state.filter.start,
log_id: r.fullname.split('|')[1],
duration: endTime - startTime,
start_time_utc_millis: startTime,
end_time_utc_millis: endTime,
segment_offsets: r.segment_start_times.map((x) => x - state.filter.start),
// TODO: get this from the API, this isn't correct for segments with a time jump
segment_durations: r.segment_start_times.map((x, i) => r.segment_end_times[i] - x),
};
});

Expand Down Expand Up @@ -374,6 +613,30 @@ export function selectTimeFilter(start, end) {
};
}

export function primeGetSubscription(dongleId, subscription) {
return {
type: Types.ACTION_PRIME_SUBSCRIPTION,
dongleId,
subscription,
};
}

export function urlForState(dongleId, log_id, start, end, prime) {
const path = [dongleId];

if (log_id) {
path.push(log_id);
if (start && end && start > 0) {
path.push(start);
path.push(end);
}
} else if (prime) {
path.push('prime');
}

return `/${path.join('/')}`;
}

export function analyticsEvent(name, parameters) {
return {
type: Types.ANALYTICS_EVENT,
Expand Down
4 changes: 2 additions & 2 deletions src/actions/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ describe('timeline actions', () => {
it('should push history state when editing zoom', () => {
const dispatch = jest.fn();
const getState = jest.fn();
const actionThunk = pushTimelineRange(123, 1234);
const actionThunk = pushTimelineRange("log_id", 123, 1234);

getState.mockImplementationOnce(() => ({
dongleId: 'statedongle',
loop: {},
zoom: {},
}));
actionThunk(dispatch, getState);
expect(push).toBeCalledWith('/statedongle/123/1234');
expect(push).toBeCalledWith('/statedongle/log_id/123/1234');
});
});
1 change: 0 additions & 1 deletion src/actions/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ export const ACTION_BUFFER_VIDEO = 'action_buffer_video';
export const ACTION_RESET = 'action_reset';

// segments
export const ACTION_UPDATE_SEGMENTS = 'update_segments';
export const ACTION_ROUTES_METADATA = 'routes_metadata';

// files
Expand Down
Loading

0 comments on commit e3925d4

Please sign in to comment.