Merge branch 'main' into gm_conversion_on_reconnect_handler
This commit is contained in:
commit
28949d5605
18 changed files with 171 additions and 94 deletions
|
|
@ -16,6 +16,35 @@ import {forceLogoutIfNecessary} from './session';
|
|||
import type {Model} from '@nozbe/watermelondb';
|
||||
import type PostModel from '@typings/database/models/servers/post';
|
||||
|
||||
export async function getIsReactionAlreadyAddedToPost(serverUrl: string, postId: string, emojiName: string) {
|
||||
try {
|
||||
const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
|
||||
const currentUserId = await getCurrentUserId(database);
|
||||
const emojiAlias = getEmojiFirstAlias(emojiName);
|
||||
return await queryReaction(database, emojiAlias, postId, currentUserId).fetchCount() > 0;
|
||||
} catch (error) {
|
||||
logDebug('error on getIsReactionAlreadyAddedToPost', getFullErrorMessage(error));
|
||||
forceLogoutIfNecessary(serverUrl, error);
|
||||
return {error};
|
||||
}
|
||||
}
|
||||
|
||||
export async function toggleReaction(serverUrl: string, postId: string, emojiName: string) {
|
||||
try {
|
||||
const isReactionAlreadyAddedToPost = await getIsReactionAlreadyAddedToPost(serverUrl, postId, emojiName);
|
||||
|
||||
if (isReactionAlreadyAddedToPost) {
|
||||
return removeReaction(serverUrl, postId, emojiName);
|
||||
}
|
||||
return addReaction(serverUrl, postId, emojiName);
|
||||
} catch (error) {
|
||||
logDebug('error on toggleReaction', getFullErrorMessage(error));
|
||||
forceLogoutIfNecessary(serverUrl, error);
|
||||
return {error};
|
||||
}
|
||||
}
|
||||
|
||||
export async function addReaction(serverUrl: string, postId: string, emojiName: string) {
|
||||
try {
|
||||
const client = NetworkManager.getClient(serverUrl);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import React, {useCallback, useRef, useState} from 'react';
|
|||
import {useIntl} from 'react-intl';
|
||||
import {Keyboard, TouchableOpacity} from 'react-native';
|
||||
|
||||
import {addReaction, removeReaction} from '@actions/remote/reactions';
|
||||
import {addReaction, removeReaction, toggleReaction} from '@actions/remote/reactions';
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import {Screens} from '@constants';
|
||||
import {MAX_ALLOWED_REACTIONS} from '@constants/emoji';
|
||||
|
|
@ -100,8 +100,8 @@ const Reactions = ({currentUserId, canAddReaction, canRemoveReaction, disabled,
|
|||
return {reactionsByName, highlightedReactions};
|
||||
}, [sortedReactions, reactions]);
|
||||
|
||||
const handleAddReactionToPost = (emoji: string) => {
|
||||
addReaction(serverUrl, postId, emoji);
|
||||
const handleToggleReactionToPost = (emoji: string) => {
|
||||
toggleReaction(serverUrl, postId, emoji);
|
||||
};
|
||||
|
||||
const handleAddReaction = useCallback(preventDoubleTap(() => {
|
||||
|
|
@ -110,7 +110,7 @@ const Reactions = ({currentUserId, canAddReaction, canRemoveReaction, disabled,
|
|||
screen: Screens.EMOJI_PICKER,
|
||||
theme,
|
||||
title: formatMessage({id: 'mobile.post_info.add_reaction', defaultMessage: 'Add Reaction'}),
|
||||
props: {onEmojiPress: handleAddReactionToPost},
|
||||
props: {onEmojiPress: handleToggleReactionToPost},
|
||||
});
|
||||
}), [formatMessage, theme]);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,4 +7,5 @@ export default {
|
|||
PLUGIN_DISMISSED_POST_ERROR: 'plugin.message_will_be_posted.dismiss_post',
|
||||
SEND_EMAIL_WITH_DEFAULTS_ERROR: 'api.team.invite_members.unable_to_send_email_with_defaults.app_error',
|
||||
TEAM_MEMBERSHIP_DENIAL_ERROR_ID: 'api.team.add_members.user_denied',
|
||||
DUPLICATE_CHANNEL_NAME: 'store.sql_channel.save_channel.exists.app_error',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -22,14 +22,22 @@ const style = StyleSheet.create({
|
|||
});
|
||||
|
||||
type Props = {
|
||||
channelId: string;
|
||||
showJoinCallBanner: boolean;
|
||||
showIncomingCalls: boolean;
|
||||
isInACall: boolean;
|
||||
channelId?: string;
|
||||
showJoinCallBanner?: boolean;
|
||||
showIncomingCalls?: boolean;
|
||||
isInACall?: boolean;
|
||||
threadScreen?: boolean;
|
||||
channelsScreen?: boolean;
|
||||
}
|
||||
|
||||
const FloatingCallContainer = ({channelId, showJoinCallBanner, showIncomingCalls, isInACall, threadScreen}: Props) => {
|
||||
const FloatingCallContainer = ({
|
||||
channelId,
|
||||
showJoinCallBanner,
|
||||
showIncomingCalls,
|
||||
isInACall,
|
||||
threadScreen,
|
||||
channelsScreen,
|
||||
}: Props) => {
|
||||
const serverUrl = useServerUrl();
|
||||
const insets = useSafeAreaInsets();
|
||||
const isTablet = useIsTablet();
|
||||
|
|
@ -39,10 +47,13 @@ const FloatingCallContainer = ({channelId, showJoinCallBanner, showIncomingCalls
|
|||
const wrapperTop = {
|
||||
top: insets.top + topBarForTablet + topBarChannel,
|
||||
};
|
||||
const wrapperBottom = {
|
||||
bottom: 8,
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={[style.wrapper, wrapperTop]}>
|
||||
{showJoinCallBanner &&
|
||||
<View style={[style.wrapper, channelsScreen ? wrapperBottom : wrapperTop]}>
|
||||
{showJoinCallBanner && channelId &&
|
||||
<JoinCallBanner
|
||||
serverUrl={serverUrl}
|
||||
channelId={channelId}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ const style = StyleSheet.create({
|
|||
});
|
||||
|
||||
type Props = {
|
||||
channelId: string;
|
||||
channelId?: string;
|
||||
}
|
||||
|
||||
export const IncomingCallsContainer = ({
|
||||
|
|
|
|||
|
|
@ -3,7 +3,13 @@
|
|||
|
||||
import {distinctUntilChanged, switchMap, combineLatest, Observable, of as of$} from 'rxjs';
|
||||
|
||||
import {observeCallsConfig, observeCallsState, observeCurrentCall} from '@calls/state';
|
||||
import {
|
||||
observeCallsConfig,
|
||||
observeCallsState,
|
||||
observeChannelsWithCalls,
|
||||
observeCurrentCall,
|
||||
observeIncomingCalls,
|
||||
} from '@calls/state';
|
||||
import {fillUserModels, userIds} from '@calls/utils';
|
||||
import {License} from '@constants';
|
||||
import DatabaseManager from '@database/manager';
|
||||
|
|
@ -88,3 +94,41 @@ export const observeCurrentSessionsDict = () => {
|
|||
)),
|
||||
) as Observable<Dictionary<CallSession>>;
|
||||
};
|
||||
|
||||
export const observeCallStateInChannel = (serverUrl: string, database: Database, channelId: Observable<string>) => {
|
||||
const isCallInCurrentChannel = combineLatest([channelId, observeChannelsWithCalls(serverUrl)]).pipe(
|
||||
switchMap(([id, calls]) => of$(Boolean(calls[id]))),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
const currentCall = observeCurrentCall();
|
||||
const ccChannelId = currentCall.pipe(
|
||||
switchMap((call) => of$(call?.channelId)),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
const isInACall = currentCall.pipe(
|
||||
switchMap((call) => of$(Boolean(call?.connected))),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
const dismissed = combineLatest([channelId, observeCallsState(serverUrl)]).pipe(
|
||||
switchMap(([id, state]) => of$(Boolean(state.calls[id]?.dismissed[state.myUserId]))),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
const isInCurrentChannelCall = combineLatest([channelId, ccChannelId]).pipe(
|
||||
switchMap(([id, ccId]) => of$(id === ccId)),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
const showJoinCallBanner = combineLatest([isCallInCurrentChannel, dismissed, isInCurrentChannelCall]).pipe(
|
||||
switchMap(([isCall, dism, inCurrCall]) => of$(Boolean(isCall && !dism && !inCurrCall))),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
const showIncomingCalls = observeIncomingCalls().pipe(
|
||||
switchMap((ics) => of$(ics.incomingCalls.length > 0)),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
|
||||
return {
|
||||
showJoinCallBanner,
|
||||
isInACall,
|
||||
showIncomingCalls,
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,15 +3,9 @@
|
|||
|
||||
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
||||
import withObservables from '@nozbe/with-observables';
|
||||
import {combineLatest, distinctUntilChanged, of as of$, switchMap} from 'rxjs';
|
||||
import {of as of$, switchMap} from 'rxjs';
|
||||
|
||||
import {observeIsCallsEnabledInChannel} from '@calls/observers';
|
||||
import {
|
||||
observeCallsState,
|
||||
observeChannelsWithCalls,
|
||||
observeCurrentCall,
|
||||
observeIncomingCalls,
|
||||
} from '@calls/state';
|
||||
import {observeCallStateInChannel, observeIsCallsEnabledInChannel} from '@calls/observers';
|
||||
import {Preferences} from '@constants';
|
||||
import {withServerUrl} from '@context/server';
|
||||
import {observeCurrentChannel} from '@queries/servers/channel';
|
||||
|
|
@ -29,37 +23,6 @@ type EnhanceProps = WithDatabaseArgs & {
|
|||
|
||||
const enhanced = withObservables([], ({database, serverUrl}: EnhanceProps) => {
|
||||
const channelId = observeCurrentChannelId(database);
|
||||
|
||||
const isCallInCurrentChannel = combineLatest([channelId, observeChannelsWithCalls(serverUrl)]).pipe(
|
||||
switchMap(([id, calls]) => of$(Boolean(calls[id]))),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
const currentCall = observeCurrentCall();
|
||||
const ccChannelId = currentCall.pipe(
|
||||
switchMap((call) => of$(call?.channelId)),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
const isInACall = currentCall.pipe(
|
||||
switchMap((call) => of$(Boolean(call?.connected))),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
const dismissed = combineLatest([channelId, observeCallsState(serverUrl)]).pipe(
|
||||
switchMap(([id, state]) => of$(Boolean(state.calls[id]?.dismissed[state.myUserId]))),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
const isInCurrentChannelCall = combineLatest([channelId, ccChannelId]).pipe(
|
||||
switchMap(([id, ccId]) => of$(id === ccId)),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
const showJoinCallBanner = combineLatest([isCallInCurrentChannel, dismissed, isInCurrentChannelCall]).pipe(
|
||||
switchMap(([isCall, dism, inCurrCall]) => of$(Boolean(isCall && !dism && !inCurrCall))),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
const showIncomingCalls = observeIncomingCalls().pipe(
|
||||
switchMap((ics) => of$(ics.incomingCalls.length > 0)),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
|
||||
const dismissedGMasDMNotice = queryPreferencesByCategoryAndName(database, Preferences.CATEGORIES.SYSTEM_NOTICE, Preferences.NOTICES.GM_AS_DM).observe();
|
||||
const channelType = observeCurrentChannel(database).pipe(switchMap((c) => of$(c?.type)));
|
||||
const currentUserId = observeCurrentUserId(database);
|
||||
|
|
@ -67,9 +30,7 @@ const enhanced = withObservables([], ({database, serverUrl}: EnhanceProps) => {
|
|||
|
||||
return {
|
||||
channelId,
|
||||
showJoinCallBanner,
|
||||
isInACall,
|
||||
showIncomingCalls,
|
||||
...observeCallStateInChannel(serverUrl, database, channelId),
|
||||
isCallsEnabledInChannel: observeIsCallsEnabledInChannel(database, serverUrl, channelId),
|
||||
dismissedGMasDMNotice,
|
||||
channelType,
|
||||
|
|
|
|||
|
|
@ -8,9 +8,10 @@ import {Text, View} from 'react-native';
|
|||
import {convertGroupMessageToPrivateChannel, switchToChannelById} from '@actions/remote/channel';
|
||||
import Button from '@components/button';
|
||||
import Loading from '@components/loading';
|
||||
import {ServerErrors} from '@constants';
|
||||
import {useServerUrl} from '@context/server';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {isErrorWithMessage} from '@utils/errors';
|
||||
import {isErrorWithMessage, isServerError} from '@utils/errors';
|
||||
import {logError} from '@utils/log';
|
||||
import {preventDoubleTap} from '@utils/tap';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
|
@ -65,6 +66,7 @@ export const ConvertGMToChannelForm = ({
|
|||
const [selectedTeam, setSelectedTeam] = useState<Team>(commonTeams[0]);
|
||||
const [newChannelName, setNewChannelName] = useState<string>('');
|
||||
const [errorMessage, setErrorMessage] = useState<string>('');
|
||||
const [channelNameErrorMessage, setChannelNameErrorMessage] = useState<string>('');
|
||||
const [conversionInProgress, setConversionInProgress] = useState(false);
|
||||
|
||||
const userDisplayNames = useMemo(() => profiles.map((profile) => displayUsername(profile, locale, teammateNameDisplay)), [profiles, teammateNameDisplay, locale]);
|
||||
|
|
@ -79,7 +81,9 @@ export const ConvertGMToChannelForm = ({
|
|||
|
||||
const {updatedChannel, error} = await convertGroupMessageToPrivateChannel(serverUrl, channelId, selectedTeam.id, newChannelName);
|
||||
if (error) {
|
||||
if (isErrorWithMessage(error)) {
|
||||
if (isServerError(error) && error.server_error_id === ServerErrors.DUPLICATE_CHANNEL_NAME && isErrorWithMessage(error)) {
|
||||
setChannelNameErrorMessage(error.message);
|
||||
} else if (isErrorWithMessage(error)) {
|
||||
setErrorMessage(error.message);
|
||||
} else {
|
||||
setErrorMessage(formatMessage({id: 'channel_info.convert_gm_to_channel.conversion_error', defaultMessage: 'Something went wrong. Failed to convert Group Message to Private Channel.'}));
|
||||
|
|
@ -153,7 +157,10 @@ export const ConvertGMToChannelForm = ({
|
|||
selectedTeamId={selectedTeam?.id}
|
||||
/>
|
||||
}
|
||||
<ChannelNameInput onChange={setNewChannelName}/>
|
||||
<ChannelNameInput
|
||||
onChange={setNewChannelName}
|
||||
error={channelNameErrorMessage}
|
||||
/>
|
||||
<Button
|
||||
onPress={handleOnPress}
|
||||
text={confirmButtonText}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import Animated, {useAnimatedStyle, withTiming} from 'react-native-reanimated';
|
|||
import {type Edge, SafeAreaView, useSafeAreaInsets} from 'react-native-safe-area-context';
|
||||
|
||||
import {refetchCurrentUser} from '@actions/remote/user';
|
||||
import FloatingCallContainer from '@calls/components/floating_call_container';
|
||||
import AnnouncementBanner from '@components/announcement_banner';
|
||||
import ConnectionBanner from '@components/connection_banner';
|
||||
import TeamSidebar from '@components/team_sidebar';
|
||||
|
|
@ -40,6 +41,7 @@ type ChannelProps = {
|
|||
coldStart?: boolean;
|
||||
currentUserId?: string;
|
||||
hasCurrentUser: boolean;
|
||||
showIncomingCalls: boolean;
|
||||
};
|
||||
|
||||
const edges: Edge[] = ['bottom', 'left', 'right'];
|
||||
|
|
@ -197,6 +199,12 @@ const ChannelListScreen = (props: ChannelProps) => {
|
|||
{isTablet &&
|
||||
<AdditionalTabletView/>
|
||||
}
|
||||
{props.showIncomingCalls && !isTablet &&
|
||||
<FloatingCallContainer
|
||||
showIncomingCalls={props.showIncomingCalls}
|
||||
channelsScreen={true}
|
||||
/>
|
||||
}
|
||||
</Animated.View>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import withObservables from '@nozbe/with-observables';
|
|||
import {of as of$} from 'rxjs';
|
||||
import {distinctUntilChanged, switchMap} from 'rxjs/operators';
|
||||
|
||||
import {observeIncomingCalls} from '@calls/state';
|
||||
import {queryAllMyChannelsForTeam} from '@queries/servers/channel';
|
||||
import {observeCurrentTeamId, observeCurrentUserId, observeLicense} from '@queries/servers/system';
|
||||
import {queryMyTeams} from '@queries/servers/team';
|
||||
|
|
@ -24,6 +25,11 @@ const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
|
|||
|
||||
const teamsCount = queryMyTeams(database).observeCount(false);
|
||||
|
||||
const showIncomingCalls = observeIncomingCalls().pipe(
|
||||
switchMap((ics) => of$(ics.incomingCalls.length > 0)),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
|
||||
return {
|
||||
isCRTEnabled: observeIsCRTEnabled(database),
|
||||
hasTeams: teamsCount.pipe(
|
||||
|
|
@ -46,6 +52,7 @@ const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
|
|||
switchMap((u) => of$(Boolean(u))),
|
||||
distinctUntilChanged(),
|
||||
),
|
||||
showIncomingCalls,
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import React, {useCallback} from 'react';
|
|||
import {useIntl} from 'react-intl';
|
||||
import {useWindowDimensions, View} from 'react-native';
|
||||
|
||||
import {addReaction} from '@actions/remote/reactions';
|
||||
import {toggleReaction} from '@actions/remote/reactions';
|
||||
import {Screens} from '@constants';
|
||||
import {
|
||||
LARGE_CONTAINER_SIZE,
|
||||
|
|
@ -57,7 +57,7 @@ const ReactionBar = ({bottomSheetId, recentEmojis = [], postId}: QuickReactionPr
|
|||
|
||||
const handleEmojiPress = useCallback(async (emoji: string) => {
|
||||
await dismissBottomSheet(bottomSheetId);
|
||||
addReaction(serverUrl, postId, emoji);
|
||||
toggleReaction(serverUrl, postId, emoji);
|
||||
}, [bottomSheetId, postId, serverUrl]);
|
||||
|
||||
const openEmojiPicker = useCallback(async () => {
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@
|
|||
|
||||
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
||||
import withObservables from '@nozbe/with-observables';
|
||||
import {distinctUntilChanged, switchMap, combineLatest, of as of$} from 'rxjs';
|
||||
import {distinctUntilChanged, switchMap, of as of$} from 'rxjs';
|
||||
|
||||
import {observeCallsState, observeChannelsWithCalls, observeCurrentCall, observeIncomingCalls} from '@calls/state';
|
||||
import {observeCallStateInChannel} from '@calls/observers';
|
||||
import {withServerUrl} from '@context/server';
|
||||
import {observePost} from '@queries/servers/post';
|
||||
import {observeIsCRTEnabled} from '@queries/servers/thread';
|
||||
|
|
@ -28,41 +28,10 @@ const enhanced = withObservables(['rootId'], ({database, serverUrl, rootId}: Enh
|
|||
switchMap((r) => of$(r?.channelId || '')),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
const isCallInCurrentChannel = combineLatest([channelId, observeChannelsWithCalls(serverUrl)]).pipe(
|
||||
switchMap(([id, calls]) => of$(Boolean(calls[id]))),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
const currentCall = observeCurrentCall();
|
||||
const ccChannelId = currentCall.pipe(
|
||||
switchMap((call) => of$(call?.channelId)),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
const isInACall = currentCall.pipe(
|
||||
switchMap((call) => of$(Boolean(call?.connected))),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
const dismissed = combineLatest([channelId, observeCallsState(serverUrl)]).pipe(
|
||||
switchMap(([id, state]) => of$(Boolean(state.calls[id]?.dismissed[state.myUserId]))),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
const isInCurrentChannelCall = combineLatest([channelId, ccChannelId]).pipe(
|
||||
switchMap(([id, ccId]) => of$(id === ccId)),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
const showJoinCallBanner = combineLatest([isCallInCurrentChannel, dismissed, isInCurrentChannelCall]).pipe(
|
||||
switchMap(([isCall, dism, inCurrCall]) => of$(Boolean(isCall && !dism && !inCurrCall))),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
const showIncomingCalls = observeIncomingCalls().pipe(
|
||||
switchMap((ics) => of$(ics.incomingCalls.length > 0)),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
|
||||
return {
|
||||
isCRTEnabled: observeIsCRTEnabled(database),
|
||||
showJoinCallBanner,
|
||||
isInACall,
|
||||
showIncomingCalls,
|
||||
...observeCallStateInChannel(serverUrl, database, channelId),
|
||||
rootId: of$(rId),
|
||||
rootPost,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -112,6 +112,8 @@
|
|||
"channel_info.archive_description.cannot_view_archived": "Dies wird den Kanal für das Team archivieren und aus der Benutzeroberfläche entfernen. Archivierte Kanäle könne bei Bedard wieder aktiviert werden. \n \nBist du dir sicher, dass du {term} {name} archivieren möchtest?",
|
||||
"channel_info.archive_failed": "Beim Versuch, den Kanal {displayName} zu archivieren, ist ein Fehler aufgetreten",
|
||||
"channel_info.archive_title": "{term} archivieren",
|
||||
"channel_info.channel_auto_follow_threads": "Allen Unterhaltungen in diesem Kanal folgen",
|
||||
"channel_info.channel_auto_follow_threads_failed": "Beim Versuch, allen Unterhaltungen im Kanal {displayName} automatisch zu verfolgen, ist ein Fehler aufgetreten",
|
||||
"channel_info.channel_files": "Dateien",
|
||||
"channel_info.close": "Schliessen",
|
||||
"channel_info.close_dm": "Direktnachricht schließen",
|
||||
|
|
|
|||
|
|
@ -166,6 +166,7 @@
|
|||
"global_threads.markAllRead.cancel": "Odustani",
|
||||
"global_threads.options.mark_as_read": "Označi kao pročitano",
|
||||
"global_threads.options.open_in_channel": "Otvori u kanalu",
|
||||
"global_threads.unreads": "Nepročitani",
|
||||
"home.header.plus_menu": "Opcije",
|
||||
"integration_selector.multiselect.submit": "Obavljeno",
|
||||
"interactive_dialog.submit": "Pošalji",
|
||||
|
|
@ -200,6 +201,8 @@
|
|||
"last_users_message.first": "{firstUser} i ",
|
||||
"last_users_message.others": "{numOthers} drugih ",
|
||||
"login.email": "E-mail",
|
||||
"login.forgot": "Ne sjećaš se lozinke?",
|
||||
"login.ldapUsername": "AD/LDAP korisničko ime",
|
||||
"login.or": "ili",
|
||||
"login.password": "Lozinka",
|
||||
"login.signIn": "Prijavi se",
|
||||
|
|
@ -228,6 +231,7 @@
|
|||
"mobile.channel_info.alertNo": "Ne",
|
||||
"mobile.channel_info.alertYes": "Da",
|
||||
"mobile.channel_list.recent": "Nedavno",
|
||||
"mobile.channel_list.unreads": "Nepročitani",
|
||||
"mobile.components.select_server_view.connect": "Poveži",
|
||||
"mobile.components.select_server_view.connecting": "Povezivanje",
|
||||
"mobile.components.select_server_view.enterServerUrl": "Upiši URL servera",
|
||||
|
|
@ -323,6 +327,9 @@
|
|||
"notification_settings.mentions": "Spominjanja",
|
||||
"notification_settings.mentions_replies": "Spominjanja i odgovori",
|
||||
"notification_settings.ooo_auto_responder": "Automatki odgovori",
|
||||
"password_send.description": "Za resetiranje lozinke upiši korištenu e-mail adresu za registraciju",
|
||||
"password_send.error": "Upiši važeću e-mail adresu.",
|
||||
"password_send.link": "Ako račun postoji, poslat će se e-mail za ponovno postavljanje lozinke na:",
|
||||
"password_send.reset": "Resetiraj tvoju lozinku",
|
||||
"permalink.error.cancel": "Odustani",
|
||||
"permalink.error.okay": "U redu",
|
||||
|
|
|
|||
|
|
@ -112,6 +112,8 @@
|
|||
"channel_info.archive_description.cannot_view_archived": "これによりチームからチャンネルがアーカイブされ、ユーザーインターフェースから削除されます。アーカイブされたチャンネルは、復元することもできます。\n \n{term} {name} を本当にアーカイブしますか?",
|
||||
"channel_info.archive_failed": "チャンネル {displayName} をアーカイブする際にエラーが発生しました",
|
||||
"channel_info.archive_title": "{term} をアーカイブする",
|
||||
"channel_info.channel_auto_follow_threads": "このチャンネルのすべてのスレッドをフォローする",
|
||||
"channel_info.channel_auto_follow_threads_failed": "このチャンネル {displayName} のすべてのスレッドを自動フォローする際にエラーが発生しました",
|
||||
"channel_info.channel_files": "ファイル",
|
||||
"channel_info.close": "閉じる",
|
||||
"channel_info.close_dm": "ダイレクトメッセージを閉じる",
|
||||
|
|
@ -119,12 +121,25 @@
|
|||
"channel_info.close_gm": "グループメッセージを閉じる",
|
||||
"channel_info.close_gm_channel": "本当にこのグループメッセージを閉じますか? ホーム画面から削除されますが、いつでも再び開くことができます。",
|
||||
"channel_info.convert_failed": "{displayName} を非公開チャンネルに変更できませんでした。",
|
||||
"channel_info.convert_gm_to_channel": "非公開チャンネルに変換する",
|
||||
"channel_info.convert_gm_to_channel.button_text": "非公開チャンネルに変換する",
|
||||
"channel_info.convert_gm_to_channel.button_text_converting": "変換中...",
|
||||
"channel_info.convert_gm_to_channel.conversion_error": "問題が発生しました。グループメッセージを非公開チャンネルへ変換できませんでした。",
|
||||
"channel_info.convert_gm_to_channel.loading.footer": "詳細を取得中...",
|
||||
"channel_info.convert_gm_to_channel.screen_title": "非公開チャンネルに変換する",
|
||||
"channel_info.convert_gm_to_channel.team_selector_list.title": "チームを選択する",
|
||||
"channel_info.convert_gm_to_channel.warning.body.yourself": "あなた自身",
|
||||
"channel_info.convert_gm_to_channel.warning.bodyXXXX": "{memberNames} とのグループメッセージをチャンネルに変換しようとしています。この操作は元に戻すことはできません。",
|
||||
"channel_info.convert_gm_to_channel.warning.header": "チャンネルメンバー全員が会話の履歴を見れるようになります",
|
||||
"channel_info.convert_gm_to_channel.warning.no_teams.body": "メンバーが同じチームに所属していないため、グループメッセージをチャンネルに変換できません。このグループメッセージをチャンネルに変換するには、すべてのメンバーを1つのチームに追加してください。",
|
||||
"channel_info.convert_gm_to_channel.warning.no_teams.header": "グループメンバーが異なるチームに所属しているため、チャンネルに変換できませんでした",
|
||||
"channel_info.convert_private": "非公開チャンネルに変更する",
|
||||
"channel_info.convert_private_description": "{displayName} を非公開チャンネルに変更すると、履歴やメンバーシップは保持されます。共有されたファイルはリンクを持つ全員がアクセス可能なままになります。非公開チャンネルのメンバーシップは招待によってのみ追加されます。\n\nこの変更は永続的なものであり、元に戻すことはできません。\n\n本当に {displayName} を非公開チャンネルに変更しますか?",
|
||||
"channel_info.convert_private_success": "{displayName} は非公開チャンネルになりました。",
|
||||
"channel_info.convert_private_title": "{displayName} を非公開チャンネルに変更しますか?",
|
||||
"channel_info.copied": "コピーしました",
|
||||
"channel_info.copy_link": "リンクをコピー",
|
||||
"channel_info.copy_purpose_text": "目的のテキストをコピーする",
|
||||
"channel_info.custom_status": "カスタムステータス:",
|
||||
"channel_info.edit_header": "ヘッダーを編集する",
|
||||
"channel_info.error_close": "閉じる",
|
||||
|
|
@ -157,6 +172,8 @@
|
|||
"channel_info.unarchive_description": "{term} {name} を本当にアーカイブから復元しますか?",
|
||||
"channel_info.unarchive_failed": "チャンネル {displayName} のアーカイブから復元する際にエラーが発生しました",
|
||||
"channel_info.unarchive_title": "{term} をアーカイブから復元",
|
||||
"channel_into.convert_gm_to_channel.team_selector.label": "チーム",
|
||||
"channel_into.convert_gm_to_channel.team_selector.placeholder": "チームを選択",
|
||||
"channel_intro.createdBy": "{user} によって {date} に作成されました",
|
||||
"channel_intro.createdOn": "{date} に作成されました",
|
||||
"channel_list.channels_category": "チャンネル",
|
||||
|
|
@ -319,6 +336,7 @@
|
|||
"general_settings.help": "ヘルプ",
|
||||
"general_settings.notifications": "通知",
|
||||
"general_settings.report_problem": "問題を報告する",
|
||||
"generic.back": "戻る",
|
||||
"get_post_link_modal.title": "リンクをコピーする",
|
||||
"global_threads.allThreads": "全スレッド",
|
||||
"global_threads.emptyThreads.message": "あなたがメンションされた、または参加したことのあるスレッドが、フォローしたスレッドとともにここに表示されます。",
|
||||
|
|
@ -611,6 +629,7 @@
|
|||
"mobile.managed.settings": "設定へ移動する",
|
||||
"mobile.markdown.code.copy_code": "コードのコピー",
|
||||
"mobile.markdown.code.plusMoreLines": "あと {count, number} {count, plural, one {line} other {lines}} あります",
|
||||
"mobile.markdown.copy_header": "ヘッダーテキストをコピーする",
|
||||
"mobile.markdown.image.too_large": "画像が {maxWidth} x {maxHeight} の上限を超えています:",
|
||||
"mobile.markdown.link.copy_url": "URL のコピー",
|
||||
"mobile.mention.copy_mention": "メンションをコピーする",
|
||||
|
|
@ -708,6 +727,9 @@
|
|||
"mobile.server_url.empty": "有効なサーバーURLを入力してください",
|
||||
"mobile.server_url.invalid_format": "URLはhttp://またはhttps://から始まらなくてはいけません",
|
||||
"mobile.session_expired.title": "セッション有効期限切れ",
|
||||
"mobile.session_expired_days": "引き続き通知を受け取るにはログインしてください。{siteName} のセッションは {daysCount, number} {daysCount, plural, one {日} other {日}} 毎に期限切れになるよう設定されています。",
|
||||
"mobile.session_expired_days_hrs": "引き続き通知を受け取るにはログインしてください。{siteName} のセッションは {daysCount, number} {daysCount, plural, one {日} other {日}} {hoursCount, number} {hoursCount, plural, one {時間} other {時間}} 毎に期限切れになるよう設定されています。",
|
||||
"mobile.session_expired_hrs": "引き続き通知を受け取るにはログインしてください。{siteName} のセッションは {hoursCount, number} {hoursCount, plural, one {時間} other {時間}} 毎に期限切れになるよう設定されています。",
|
||||
"mobile.set_status.dnd": "取り込み中",
|
||||
"mobile.storage_permission_denied_description": "サーバーにファイルをアップロードします。設定を開き、{applicationName} にこのデバイスのファイルへの読み取りと書き込みのアクセス権を付与してください。",
|
||||
"mobile.storage_permission_denied_title": "{applicationName} があなたのファイルへアクセスしようとしています",
|
||||
|
|
|
|||
|
|
@ -112,6 +112,8 @@
|
|||
"channel_info.archive_description.cannot_view_archived": "Dit archiveert het kanaal van het team en verwijdert het uit de gebruikersinterface. Gearchiveerde kanalen kunnen worden gearchiveerd als ze weer nodig zijn.\n\nWeet je zeker dat je de {term} {name} wil archiveren?",
|
||||
"channel_info.archive_failed": "Er is een fout opgetreden bij het archiveren van het kanaal {displayName}",
|
||||
"channel_info.archive_title": "Archiveer {term}",
|
||||
"channel_info.channel_auto_follow_threads": "Volg alle draadjes in dit kanaal",
|
||||
"channel_info.channel_auto_follow_threads_failed": "Er is een fout opgetreden bij het automatisch volgen van alle draadjes in kanaal {displayName}",
|
||||
"channel_info.channel_files": "Bestanden",
|
||||
"channel_info.close": "Sluiten",
|
||||
"channel_info.close_dm": "Direct bericht sluiten",
|
||||
|
|
|
|||
|
|
@ -112,6 +112,8 @@
|
|||
"channel_info.archive_description.cannot_view_archived": "Spowoduje to zarchiwizowanie kanału z zespołu i usunięcie go z interfejsu użytkownika. Zarchiwizowane kanały mogą zostać odarchiwizowane, jeśli będą ponownie potrzebne.\n\nCzy na pewno chcesz zarchiwizować kanał {term} {name}?",
|
||||
"channel_info.archive_failed": "Wystąpił błąd podczas próby archiwizacji kanału {displayName}",
|
||||
"channel_info.archive_title": "Archiwizuj {term}",
|
||||
"channel_info.channel_auto_follow_threads": "Śledź wszystkie wątki na tym kanale",
|
||||
"channel_info.channel_auto_follow_threads_failed": "Wystąpił błąd podczas próby automatycznego śledzenia wszystkich wątków na kanale {displayName}",
|
||||
"channel_info.channel_files": "Pliki",
|
||||
"channel_info.close": "Zamknij",
|
||||
"channel_info.close_dm": "Zamknij wiadomość bezpośrednią",
|
||||
|
|
@ -710,6 +712,9 @@
|
|||
"mobile.server_url.empty": "Proszę wprowadzić prawidłowy adres URL serwera",
|
||||
"mobile.server_url.invalid_format": "Adres musi zaczynać się od http:// lub https://",
|
||||
"mobile.session_expired.title": "Sesja Wygasła",
|
||||
"mobile.session_expired_days": "Zaloguj się, aby nadal otrzymywać powiadomienia. Sesje dla {siteName} są skonfigurowane tak, aby wygasały co {daysCount, number} {daysCount, plural, one {dzień} other {dni}}.",
|
||||
"mobile.session_expired_days_hrs": "Zaloguj się, aby nadal otrzymywać powiadomienia. Sesje dla {siteName} są skonfigurowane tak, aby wygasały co {daysCount, number} {daysCount, plural, one {dzień} other {dni}} i {hoursCount, number} {hoursCount, plural, one {godzinę} other {godzin}}.",
|
||||
"mobile.session_expired_hrs": "Zaloguj się, aby nadal otrzymywać powiadomienia. Sesje dla {siteName} są skonfigurowane tak, aby wygasały co {hoursCount, number} {hoursCount, plural, one {godzinę} other {godziny/godzin}}.",
|
||||
"mobile.set_status.dnd": "Nie przeszkadzać",
|
||||
"mobile.storage_permission_denied_description": "Prześlij pliki na swój serwer. Otwórz Ustawienia, aby przyznać {applicationName} dostęp do odczytu i zapisu plików na tym urządzeniu.",
|
||||
"mobile.storage_permission_denied_title": "{applicationName} chce otrzymać dostęp do twoich plików",
|
||||
|
|
|
|||
|
|
@ -112,6 +112,8 @@
|
|||
"channel_info.archive_description.cannot_view_archived": "Detta kommer att arkivera kanalen från teamet och ta bort den från användargränssnittet. Arkiverade kanaler kan hämtas från arkivet om de behövs igen.\n\nÄr du säker på att du vill arkivera {term} {name}?",
|
||||
"channel_info.archive_failed": "Ett fel inträffade när kanalen {displayName} skulle arkiveras",
|
||||
"channel_info.archive_title": "Arkivera {term}",
|
||||
"channel_info.channel_auto_follow_threads": "Följ alla trådar i den här kanalen",
|
||||
"channel_info.channel_auto_follow_threads_failed": "Ett fel uppstod när alla trådar i kanalen {displayName} skulle följas",
|
||||
"channel_info.channel_files": "Filer",
|
||||
"channel_info.close": "Stäng",
|
||||
"channel_info.close_dm": "Stäng direktmeddelande",
|
||||
|
|
|
|||
Loading…
Reference in a new issue